From 18511f25ec4831c86fb8ab3030263d1e65cddb62 Mon Sep 17 00:00:00 2001 From: Himanshu Gupta Date: Tue, 3 Jan 2023 11:46:04 +0000 Subject: [PATCH 1/2] Adding a new permission to interact across clone profile apps. This permision would only be available to system and 3P apps would be restricted from using it. OEMs can use this permission for their properietry apps installed with the OEM image. Test: Manual verification by triggering system sharesheet. Change-Id: I65e57cd5b4d79d3e090817ac3040844f6aa3e600 --- core/api/system-current.txt | 1 + core/res/AndroidManifest.xml | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 4e0e4b9d1ec79..314e27050423b 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -248,6 +248,7 @@ package android { field public static final String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT"; field public static final String PROVISION_DEMO_DEVICE = "android.permission.PROVISION_DEMO_DEVICE"; field public static final String QUERY_ADMIN_POLICY = "android.permission.QUERY_ADMIN_POLICY"; + field public static final String QUERY_CLONED_APPS = "android.permission.QUERY_CLONED_APPS"; field @Deprecated public static final String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES"; field public static final String QUERY_USERS = "android.permission.QUERY_USERS"; field public static final String RADIO_SCAN_WITHOUT_LOCATION = "android.permission.RADIO_SCAN_WITHOUT_LOCATION"; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 3f18e5a838cb5..f74ebc49760c3 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -7040,6 +7040,15 @@ android:protectionLevel="signature|knownSigner" android:knownCerts="@array/config_healthConnectMigrationKnownSigners" /> + + + From e37b6cffbd6c74df01255f065a2f27466b7e7442 Mon Sep 17 00:00:00 2001 From: Sarup Dalwani Date: Thu, 6 Oct 2022 08:32:13 +0000 Subject: [PATCH 2/2] Changes in queryIntentAcitivities api for intent redirection Changing behavior of queryIntentAcitivities where for api would return results across owner and clone profile if and only if caller sets flag MATCH_CLONE_PROFILE and have permission QUERY_CLONED_APPS. For other callers the api would only return results from current profile. Bug: 251373122 Test: Tested manually, CTS test cases to follow in next CL. Change-Id: I17fed609efda3cb1a6a142891ff7c44c8254c9f5 --- core/api/system-current.txt | 1 + .../android/content/pm/PackageManager.java | 14 ++++++ .../com/android/server/pm/ComputerEngine.java | 4 +- .../pm/CrossProfileIntentResolverEngine.java | 47 +++++++++++-------- .../server/pm/NoFilteringResolver.java | 26 ++++++++-- 5 files changed, 68 insertions(+), 24 deletions(-) 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; + } }