Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 709517e8 authored by Alex Kershaw's avatar Alex Kershaw Committed by Android (Google) Code Review
Browse files

Merge "Add CrossProfileApps#startActivity which takes an Intent."

parents 0b19cd94 d72fc8d3
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -11490,6 +11490,7 @@ package android.content.pm {
    method @NonNull public android.graphics.drawable.Drawable getProfileSwitchingIconDrawable(@NonNull android.os.UserHandle);
    method @NonNull public CharSequence getProfileSwitchingLabel(@NonNull android.os.UserHandle);
    method @NonNull public java.util.List<android.os.UserHandle> getTargetUserProfiles();
    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);
    method public void startMainActivity(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
    field public static final String ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED = "android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED";
  }
+26 −0
Original line number Diff line number Diff line
@@ -93,6 +93,32 @@ public class CrossProfileApps {
        }
    }

    /**
     * Starts the specified activity of the caller package in the specified profile.
     *
     * <p>The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
     * permission and both the caller and target user profiles must be in the same profile group.
     *
     * @param intent The intent to launch. A component in the caller package must be specified.
     * @param targetUser The {@link UserHandle} of the profile; must be one of the users returned by
     *        {@link #getTargetUserProfiles()} if different to the calling user, otherwise a
     *        {@link SecurityException} will be thrown.
     */
    @RequiresPermission(anyOf = {
            android.Manifest.permission.INTERACT_ACROSS_PROFILES,
            android.Manifest.permission.INTERACT_ACROSS_USERS})
    public void startActivity(@NonNull Intent intent, @NonNull UserHandle targetUser) {
        try {
            mService.startActivityAsUserByIntent(
                    mContext.getIApplicationThread(),
                    mContext.getPackageName(),
                    intent,
                    targetUser.getIdentifier());
        } 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
+2 −0
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ import android.os.UserHandle;
interface ICrossProfileApps {
    void startActivityAsUser(in IApplicationThread caller, in String callingPackage,
            in ComponentName component, int userId, boolean launchMainActivity);
    void startActivityAsUserByIntent(in IApplicationThread caller, in String callingPackage,
                        in Intent intent, int userId);
    List<UserHandle> getTargetUserProfiles(in String callingPackage);
    boolean canInteractAcrossProfiles(in String callingPackage);
    boolean canRequestInteractAcrossProfiles(in String callingPackage);
+70 −0
Original line number Diff line number Diff line
@@ -172,6 +172,50 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
                userId);
    }

    @Override
    public void startActivityAsUserByIntent(
            IApplicationThread caller,
            String callingPackage,
            Intent intent,
            @UserIdInt int userId) throws RemoteException {
        Objects.requireNonNull(callingPackage);
        Objects.requireNonNull(intent);
        Objects.requireNonNull(intent.getComponent(), "The intent must have a Component set");

        verifyCallingPackage(callingPackage);

        final int callerUserId = mInjector.getCallingUserId();
        final int callingUid = mInjector.getCallingUid();

        List<UserHandle> allowedTargetUsers = getTargetUserProfilesUnchecked(
                callingPackage, callerUserId);
        if (callerUserId != userId && !allowedTargetUsers.contains(UserHandle.of(userId))) {
            throw new SecurityException(callingPackage + " cannot access unrelated user " + userId);
        }

        Intent launchIntent = new Intent(intent);
        launchIntent.setPackage(callingPackage);

        if (!callingPackage.equals(launchIntent.getComponent().getPackageName())) {
            throw new SecurityException(
                    callingPackage + " attempts to start an activity in other package - "
                            + launchIntent.getComponent().getPackageName());
        }

        if (callerUserId != userId) {
            if (!hasInteractAcrossProfilesPermission(callingPackage)) {
                throw new SecurityException("Attempt to launch activity without required "
                        + android.Manifest.permission.INTERACT_ACROSS_PROFILES + " permission"
                        + " or target user is not in the same profile group.");
            }
        }

        verifyActivityCanHandleIntent(launchIntent, callingUid, userId);

        mInjector.getActivityTaskManagerInternal().startActivityAsUser(
                caller, callingPackage, launchIntent, /* options= */ null, userId);
    }

    @Override
    public boolean canRequestInteractAcrossProfiles(String callingPackage) {
        Objects.requireNonNull(callingPackage);
@@ -214,6 +258,11 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
        if (targetUserProfiles.isEmpty()) {
            return false;
        }

        return hasInteractAcrossProfilesPermission(callingPackage);
    }

    private boolean hasInteractAcrossProfilesPermission(String callingPackage) {
        final int callingUid = mInjector.getCallingUid();
        final int callingPid = mInjector.getCallingPid();
        return isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid)
@@ -275,6 +324,27 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
        }
    }

    private void verifyActivityCanHandleIntent(
            Intent launchIntent, int callingUid, @UserIdInt int userId) {
        final long ident = mInjector.clearCallingIdentity();
        try {
            final List<ResolveInfo> activities =
                    mInjector.getPackageManagerInternal().queryIntentActivities(
                            launchIntent,
                            launchIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
                            MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                            callingUid,
                            userId);

            if (!activities.isEmpty()) {
                return;
            }
            throw new SecurityException("Activity cannot handle intent");
        } finally {
            mInjector.restoreCallingIdentity(ident);
        }
    }

    /**
     * Verify that the specified intent does resolved to the specified component and the resolved
     * activity is exported.