diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 314e27050423b..81d8e24ae8dae 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3793,6 +3793,7 @@ package android.content.pm { field @Deprecated public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1 field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff field public static final int MATCH_ANY_USER = 4194304; // 0x400000 + field public static final int MATCH_CLONE_PROFILE = 536870912; // 0x20000000 field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000 field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000 field public static final int MATCH_INSTANT = 8388608; // 0x800000 diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 900454d59e507..895ffefe09e06 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -783,6 +783,7 @@ public abstract class PackageManager { GET_DISABLED_COMPONENTS, GET_DISABLED_UNTIL_USED_COMPONENTS, GET_UNINSTALLED_PACKAGES, + MATCH_CLONE_PROFILE }) @Retention(RetentionPolicy.SOURCE) public @interface ResolveInfoFlagsBits {} @@ -1112,6 +1113,19 @@ public abstract class PackageManager { */ public static final int MATCH_DIRECT_BOOT_AUTO = 0x10000000; + /** + * {@link ResolveInfo} flag: allow matching components across clone profile + *

+ * This flag is used only for query and not resolution, the default behaviour would be to + * restrict querying across clone profile. This flag would be honored only if caller have + * permission {@link Manifest.permission.QUERY_CLONED_APPS}. + * + * @hide + *

+ */ + @SystemApi + public static final int MATCH_CLONE_PROFILE = 0x20000000; + /** * {@link PackageInfo} flag: return all attributions declared in the package manifest */ diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 2752104371435..c2f0f52623051 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -766,7 +766,7 @@ public class ComputerEngine implements Computer { */ crossProfileResults = mCrossProfileIntentResolverEngine.resolveIntent(this, intent, resolvedType, userId, flags, pkgName, hasNonNegativePriorityResult, - mSettings::getPackage); + resolveForStart, mSettings::getPackage); if (intent.hasWebURI() || !crossProfileResults.isEmpty()) sortResult = true; } else { final PackageStateInternal setting = @@ -791,7 +791,7 @@ public class ComputerEngine implements Computer { */ crossProfileResults = mCrossProfileIntentResolverEngine.resolveIntent(this, intent, resolvedType, userId, flags, pkgName, false, - mSettings::getPackage); + resolveForStart, mSettings::getPackage); } /* diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentResolverEngine.java b/services/core/java/com/android/server/pm/CrossProfileIntentResolverEngine.java index 397fdd8e34a10..e149b04a3e4b9 100644 --- a/services/core/java/com/android/server/pm/CrossProfileIntentResolverEngine.java +++ b/services/core/java/com/android/server/pm/CrossProfileIntentResolverEngine.java @@ -84,15 +84,16 @@ public class CrossProfileIntentResolverEngine { * @param pkgName the application package name this Intent is limited to. * @param hasNonNegativePriorityResult signifies if current profile have any non-negative(active * and valid) ResolveInfo in current profile. + * @param resolveForStart true if resolution occurs to start an activity. * @param pkgSettingFunction function to find PackageStateInternal for given package * @return list of {@link CrossProfileDomainInfo} from linked profiles. */ public List resolveIntent(@NonNull Computer computer, Intent intent, String resolvedType, int userId, long flags, String pkgName, - boolean hasNonNegativePriorityResult, + boolean hasNonNegativePriorityResult, boolean resolveForStart, Function pkgSettingFunction) { return resolveIntentInternal(computer, intent, resolvedType, userId, userId, flags, pkgName, - hasNonNegativePriorityResult, pkgSettingFunction, null); + hasNonNegativePriorityResult, resolveForStart, pkgSettingFunction, null); } /** @@ -113,13 +114,14 @@ public class CrossProfileIntentResolverEngine { * @param pkgName the application package name this Intent is limited to. * @param hasNonNegativePriorityResult signifies if current profile have any non-negative(active * and valid) ResolveInfo in current profile. + * @param resolveForStart true if resolution occurs to start an activity. * @param pkgSettingFunction function to find PackageStateInternal for given package * @param visitedUserIds users for which we have already performed resolution * @return list of {@link CrossProfileDomainInfo} from linked profiles. */ private List resolveIntentInternal(@NonNull Computer computer, Intent intent, String resolvedType, int sourceUserId, int userId, long flags, - String pkgName, boolean hasNonNegativePriorityResult, + String pkgName, boolean hasNonNegativePriorityResult, boolean resolveForStart, Function pkgSettingFunction, Set visitedUserIds) { @@ -184,7 +186,8 @@ public class CrossProfileIntentResolverEngine { // Choosing strategy based on source and target user CrossProfileResolver crossProfileResolver = - chooseCrossProfileResolver(computer, userId, targetUserId); + chooseCrossProfileResolver(computer, userId, targetUserId, + resolveForStart, flags); /* If {@link CrossProfileResolver} is available for source,target pair we will call it to @@ -217,8 +220,8 @@ public class CrossProfileIntentResolverEngine { if (allowChainedResolution) { crossProfileDomainInfos.addAll(resolveIntentInternal(computer, intent, resolvedType, sourceUserId, targetUserId, flags, pkgName, - hasNonNegativePriority(crossProfileInfos), pkgSettingFunction, - visitedUserIds)); + hasNonNegativePriority(crossProfileInfos), resolveForStart, + pkgSettingFunction, visitedUserIds)); } } @@ -233,18 +236,21 @@ public class CrossProfileIntentResolverEngine { * @param computer {@link Computer} instance used for resolution by {@link ComponentResolverApi} * @param sourceUserId source user * @param targetUserId target user + * @param resolveForStart true if resolution occurs to start an activity. + * @param flags used for intent resolver selection * @return {@code CrossProfileResolver} which has value if source and target have * strategy configured otherwise null. */ @SuppressWarnings("unused") private CrossProfileResolver chooseCrossProfileResolver(@NonNull Computer computer, - @UserIdInt int sourceUserId, @UserIdInt int targetUserId) { + @UserIdInt int sourceUserId, @UserIdInt int targetUserId, boolean resolveForStart, + long flags) { /** * If source or target user is clone profile, using {@link NoFilteringResolver} * We would return NoFilteringResolver only if it is allowed(feature flag is set). */ if (shouldUseNoFilteringResolver(sourceUserId, targetUserId)) { - if (NoFilteringResolver.isIntentRedirectionAllowed()) { + if (NoFilteringResolver.isIntentRedirectionAllowed(mContext, resolveForStart, flags)) { return new NoFilteringResolver(computer.getComponentResolver(), mUserManager); } else { @@ -384,7 +390,6 @@ public class CrossProfileIntentResolverEngine { ephemeral activities. */ candidates = resolveInfoFromCrossProfileDomainInfo(crossProfileCandidates); - return new QueryIntentActivitiesResult(computer.applyPostResolutionFilter(candidates, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent)); @@ -404,11 +409,10 @@ public class CrossProfileIntentResolverEngine { */ candidates = filterCandidatesWithDomainPreferredActivitiesLPr(computer, intent, matchFlags, candidates, crossProfileCandidates, userId, - areWebInstantAppsDisabled, pkgSettingFunction); + areWebInstantAppsDisabled, resolveForStart, pkgSettingFunction); } else { candidates.addAll(resolveInfoFromCrossProfileDomainInfo(crossProfileCandidates)); } - return new QueryIntentActivitiesResult(sortResult, addInstant, candidates); } @@ -421,13 +425,14 @@ public class CrossProfileIntentResolverEngine { * @param crossProfileCandidates crossProfileDomainInfos from cross profile, it have ResolveInfo * @param userId user id of source user * @param areWebInstantAppsDisabled true if web instant apps are disabled + * @param resolveForStart true if intent is for resolution * @param pkgSettingFunction function to find PackageStateInternal for given package * @return list of ResolveInfo */ private List filterCandidatesWithDomainPreferredActivitiesLPr(Computer computer, Intent intent, long matchFlags, List candidates, List crossProfileCandidates, int userId, - boolean areWebInstantAppsDisabled, + boolean areWebInstantAppsDisabled, boolean resolveForStart, Function pkgSettingFunction) { final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0; @@ -439,7 +444,7 @@ public class CrossProfileIntentResolverEngine { final List result = filterCandidatesWithDomainPreferredActivitiesLPrBody(computer, intent, matchFlags, candidates, crossProfileCandidates, userId, areWebInstantAppsDisabled, - debug, pkgSettingFunction); + debug, resolveForStart, pkgSettingFunction); if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) { Slog.v(TAG, "Filtered results with preferred activities. New candidates count: " @@ -461,13 +466,14 @@ public class CrossProfileIntentResolverEngine { * @param userId user id of source user * @param areWebInstantAppsDisabled true if web instant apps are disabled * @param debug true if resolution logs needed to be printed + * @param resolveForStart true if intent is for resolution * @param pkgSettingFunction function to find PackageStateInternal for given package * @return list of resolve infos */ private List filterCandidatesWithDomainPreferredActivitiesLPrBody( Computer computer, Intent intent, long matchFlags, List candidates, List crossProfileCandidates, int userId, - boolean areWebInstantAppsDisabled, boolean debug, + boolean areWebInstantAppsDisabled, boolean debug, boolean resolveForStart, Function pkgSettingFunction) { final ArrayList result = new ArrayList<>(); final ArrayList matchAllList = new ArrayList<>(); @@ -525,7 +531,7 @@ public class CrossProfileIntentResolverEngine { // calling cross profile strategy to filter corresponding results result.addAll(filterCrossProfileCandidatesWithDomainPreferredActivities(computer, intent, matchFlags, categorizeResolveInfoByTargetUser, userId, - DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE)); + DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE, resolveForStart)); includeBrowser = true; } else { Pair, Integer> infosAndLevel = mDomainVerificationManager @@ -539,7 +545,7 @@ public class CrossProfileIntentResolverEngine { // calling cross profile strategy to filter corresponding results result.addAll(filterCrossProfileCandidatesWithDomainPreferredActivities(computer, intent, matchFlags, categorizeResolveInfoByTargetUser, userId, - DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE)); + DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE, resolveForStart)); } else { result.addAll(approvedInfos); @@ -547,7 +553,7 @@ public class CrossProfileIntentResolverEngine { // calling cross profile strategy to filter corresponding results result.addAll(filterCrossProfileCandidatesWithDomainPreferredActivities(computer, intent, matchFlags, categorizeResolveInfoByTargetUser, userId, - highestApproval)); + highestApproval, resolveForStart)); } } @@ -612,11 +618,13 @@ public class CrossProfileIntentResolverEngine { * CrossProfileDomainInfos * @param sourceUserId user id for intent * @param highestApprovalLevel domain approval level + * @param resolveForStart true if intent is for resolution * @return list of ResolveInfos */ private List filterCrossProfileCandidatesWithDomainPreferredActivities( Computer computer, Intent intent, long flags, SparseArray> - categorizeResolveInfoByTargetUser, int sourceUserId, int highestApprovalLevel) { + categorizeResolveInfoByTargetUser, int sourceUserId, int highestApprovalLevel, + boolean resolveForStart) { List crossProfileDomainInfos = new ArrayList<>(); @@ -629,7 +637,8 @@ public class CrossProfileIntentResolverEngine { // finding cross profile strategy based on source and target user CrossProfileResolver crossProfileIntentResolver = chooseCrossProfileResolver(computer, sourceUserId, - categorizeResolveInfoByTargetUser.keyAt(index)); + categorizeResolveInfoByTargetUser.keyAt(index), resolveForStart, + flags); // if strategy is available call it and add its filtered results if (crossProfileIntentResolver != null) { crossProfileDomainInfos.addAll(crossProfileIntentResolver diff --git a/services/core/java/com/android/server/pm/NoFilteringResolver.java b/services/core/java/com/android/server/pm/NoFilteringResolver.java index 492f9158be85e..999706a431144 100644 --- a/services/core/java/com/android/server/pm/NoFilteringResolver.java +++ b/services/core/java/com/android/server/pm/NoFilteringResolver.java @@ -16,7 +16,10 @@ package com.android.server.pm; +import android.Manifest; +import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Binder; import android.provider.DeviceConfig; @@ -47,13 +50,19 @@ public class NoFilteringResolver extends CrossProfileResolver { /** * Returns true if intent redirection for clone profile feature flag is set - * @return value of flag allow_intent_redirection_for_clone_profile + * and if its query, then check if calling user have necessary permission + * (android.permission.QUERY_CLONED_APPS) as well as required flag + * (PackageManager.MATCH_CLONE_PROFILE) bit set. + * @return true if resolver would be used for cross profile resolution. */ - public static boolean isIntentRedirectionAllowed() { + public static boolean isIntentRedirectionAllowed(Context context, + boolean resolveForStart, long flags) { final long token = Binder.clearCallingIdentity(); try { return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_APP_CLONING, - FLAG_ALLOW_INTENT_REDIRECTION_FOR_CLONE_PROFILE, false /* defaultValue */); + FLAG_ALLOW_INTENT_REDIRECTION_FOR_CLONE_PROFILE, false /* defaultValue */) + && (resolveForStart || (((flags & PackageManager.MATCH_CLONE_PROFILE) != 0) + && hasPermission(context, Manifest.permission.QUERY_CLONED_APPS))); } finally { Binder.restoreCallingIdentity(token); } @@ -123,4 +132,15 @@ public class NoFilteringResolver extends CrossProfileResolver { // no filtering return crossProfileDomainInfos; } + + /** + * Checks if calling uid have the mentioned permission + * @param context calling context + * @param permission permission name + * @return true if uid have the permission + */ + private static boolean hasPermission(Context context, String permission) { + return context.checkCallingOrSelfPermission(permission) + == PackageManager.PERMISSION_GRANTED; + } }