Scope package manager queries for ephemeral apps
Ephemeral apps can only see their own components or those components
exposed via the "visibleToEphemeral" XML attribute.
Normal apps can only see other normal apps. There is no way to expose
ephemeral components to normal apps.
Bug: 33458220
Test: Manually install ephemeral/normal apps and ensure they can only see appropriate components
Change-Id: I6ae65fd2a6ddc9aa9691f02cd55d4953048966b0
diff --git a/api/current.txt b/api/current.txt
index 5a2df57..583ca92 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1425,6 +1425,7 @@
field public static final int viewportWidth = 16843778; // 0x1010402
field public static final int visibility = 16842972; // 0x10100dc
field public static final int visible = 16843156; // 0x1010194
+ field public static final int visibleToEphemeral = 16844095; // 0x101053f
field public static final int vmSafeMode = 16843448; // 0x10102b8
field public static final int voiceIcon = 16843908; // 0x1010484
field public static final int voiceLanguage = 16843349; // 0x1010255
diff --git a/api/system-current.txt b/api/system-current.txt
index 9d113db..c49dcb4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1536,6 +1536,7 @@
field public static final int viewportWidth = 16843778; // 0x1010402
field public static final int visibility = 16842972; // 0x10100dc
field public static final int visible = 16843156; // 0x1010194
+ field public static final int visibleToEphemeral = 16844095; // 0x101053f
field public static final int vmSafeMode = 16843448; // 0x10102b8
field public static final int voiceIcon = 16843908; // 0x1010484
field public static final int voiceLanguage = 16843349; // 0x1010255
diff --git a/api/test-current.txt b/api/test-current.txt
index 20b723d..3eaa381e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1425,6 +1425,7 @@
field public static final int viewportWidth = 16843778; // 0x1010402
field public static final int visibility = 16842972; // 0x10100dc
field public static final int visible = 16843156; // 0x1010194
+ field public static final int visibleToEphemeral = 16844095; // 0x101053f
field public static final int vmSafeMode = 16843448; // 0x10102b8
field public static final int voiceIcon = 16843908; // 0x1010484
field public static final int voiceLanguage = 16843349; // 0x1010255
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index f5a79c8..2c97ec4 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -282,6 +282,10 @@
private int mVerifyState;
+ /** Whether or not the intent filter is visible to ephemeral apps. */
+ private boolean mVisibleToEphemeral;
+ /** Whether or not the intent filter is part of an ephemeral app. */
+ private boolean mEphemeral;
// These functions are the start of more optimized code for managing
// the string sets... not yet implemented.
@@ -647,6 +651,24 @@
if (verified) mVerifyState |= STATE_VERIFIED;
}
+ /** @hide */
+ public void setVisibleToEphemeral(boolean visibleToEmphemeral) {
+ mVisibleToEphemeral = visibleToEmphemeral;
+ }
+ /** @hide */
+ public boolean isVisibleToEphemeral() {
+ return mVisibleToEphemeral;
+ }
+
+ /** @hide */
+ public void setEphemeral(boolean ephemeral) {
+ mEphemeral = ephemeral;
+ }
+ /** @hide */
+ public boolean isEphemeral() {
+ return mEphemeral;
+ }
+
/**
* Add a new Intent action to match against. If any actions are included
* in the filter, then an Intent's action must be one of those values for
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 5d90acc..44dff00 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -363,6 +363,12 @@
public static final int FLAG_ON_TOP_LAUNCHER = 0x80000;
/**
+ * Bit in {@link #flags} indicating if the activity is visible to ephemeral applications.
+ * @hide
+ */
+ public static final int FLAG_VISIBLE_TO_EPHEMERAL = 0x100000;
+
+ /**
* @hide Bit in {@link #flags}: If set, this component will only be seen
* by the system user. Only works with broadcast receivers. Set from the
* android.R.attr#systemUserOnly attribute.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 3f052d38..b4e60d8 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -449,6 +449,20 @@
public static final int MATCH_KNOWN_PACKAGES = MATCH_UNINSTALLED_PACKAGES | MATCH_ANY_USER;
/**
+ * Internal {@link PackageInfo} flag: include components that are part of an
+ * ephemeral app. By default, ephemeral components are not matched.
+ * @hide
+ */
+ public static final int MATCH_EPHEMERAL = 0x00800000;
+
+ /**
+ * Internal {@link PackageInfo} flag: include only components that are exposed to
+ * ephemeral apps.
+ * @hide
+ */
+ public static final int MATCH_VISIBLE_TO_EPHEMERAL_ONLY = 0x01000000;
+
+ /**
* Internal flag used to indicate that a system component has done their
* homework and verified that they correctly handle packages and components
* that come and go over time. In particular:
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 9b2dd68..d7c3722 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3780,6 +3780,13 @@
ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
}
+ final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
+ final boolean visibleToEphemeral = isEphemeral
+ || sa.getBoolean(R.styleable.AndroidManifestActivity_visibleToEphemeral, false);
+ if (visibleToEphemeral) {
+ a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+ }
+
sa.recycle();
if (receiver && (owner.applicationInfo.privateFlags
@@ -3806,9 +3813,12 @@
if (parser.getName().equals("intent-filter")) {
ActivityIntentInfo intent = new ActivityIntentInfo(a);
- if (!parseIntent(res, parser, true, true, intent, outError)) {
+ if (!parseIntent(res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/,
+ intent, outError)) {
return null;
}
+ intent.setEphemeral(isEphemeral);
+ intent.setVisibleToEphemeral(visibleToEphemeral);
if (intent.countActions() == 0) {
Slog.w(TAG, "No actions in intent filter at "
+ mArchiveSourcePath + " "
@@ -3818,9 +3828,12 @@
}
} else if (!receiver && parser.getName().equals("preferred")) {
ActivityIntentInfo intent = new ActivityIntentInfo(a);
- if (!parseIntent(res, parser, false, false, intent, outError)) {
+ if (!parseIntent(res, parser, false /*allowGlobs*/, false /*allowAutoVerify*/,
+ intent, outError)) {
return null;
}
+ intent.setEphemeral(isEphemeral);
+ intent.setVisibleToEphemeral(visibleToEphemeral);
if (intent.countActions() == 0) {
Slog.w(TAG, "No actions in preferred at "
+ mArchiveSourcePath + " "
@@ -4071,6 +4084,10 @@
}
}
+ final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
+ final boolean visibleToEphemeral = isEphemeral
+ || ((a.info.flags & ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL) != 0);
+
sa.recycle();
if (outError[0] != null) {
@@ -4088,7 +4105,8 @@
if (parser.getName().equals("intent-filter")) {
ActivityIntentInfo intent = new ActivityIntentInfo(a);
- if (!parseIntent(res, parser, true, true, intent, outError)) {
+ if (!parseIntent(res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/,
+ intent, outError)) {
return null;
}
if (intent.countActions() == 0) {
@@ -4096,6 +4114,8 @@
+ mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
+ intent.setEphemeral(isEphemeral);
+ intent.setVisibleToEphemeral(visibleToEphemeral);
a.intents.add(intent);
}
} else if (parser.getName().equals("meta-data")) {
@@ -4233,6 +4253,13 @@
ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
}
+ final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
+ final boolean visibleToEphemeral = isEphemeral
+ || sa.getBoolean(R.styleable.AndroidManifestProvider_visibleToEphemeral, false);
+ if (visibleToEphemeral) {
+ p.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+ }
+
sa.recycle();
if ((owner.applicationInfo.privateFlags&ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)
@@ -4255,15 +4282,15 @@
}
p.info.authority = cpname.intern();
- if (!parseProviderTags(res, parser, p, outError)) {
+ if (!parseProviderTags(res, parser, isEphemeral, visibleToEphemeral, p, outError)) {
return null;
}
return p;
}
- private boolean parseProviderTags(Resources res,
- XmlResourceParser parser, Provider outInfo, String[] outError)
+ private boolean parseProviderTags(Resources res, XmlResourceParser parser,
+ boolean isEphemeral, boolean visibleToEphemeral, Provider outInfo, String[] outError)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -4276,9 +4303,12 @@
if (parser.getName().equals("intent-filter")) {
ProviderIntentInfo intent = new ProviderIntentInfo(outInfo);
- if (!parseIntent(res, parser, true, false, intent, outError)) {
+ if (!parseIntent(res, parser, true /*allowGlobs*/, false /*allowAutoVerify*/,
+ intent, outError)) {
return false;
}
+ intent.setEphemeral(isEphemeral);
+ intent.setVisibleToEphemeral(visibleToEphemeral);
outInfo.intents.add(intent);
} else if (parser.getName().equals("meta-data")) {
@@ -4526,6 +4556,13 @@
ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
}
+ final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
+ final boolean visibleToEphemeral = isEphemeral
+ || sa.getBoolean(R.styleable.AndroidManifestService_visibleToEphemeral, false);
+ if (visibleToEphemeral) {
+ s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+ }
+
sa.recycle();
if ((owner.applicationInfo.privateFlags&ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)
@@ -4549,9 +4586,12 @@
if (parser.getName().equals("intent-filter")) {
ServiceIntentInfo intent = new ServiceIntentInfo(s);
- if (!parseIntent(res, parser, true, false, intent, outError)) {
+ if (!parseIntent(res, parser, true /*allowGlobs*/, false /*allowAutoVerify*/,
+ intent, outError)) {
return null;
}
+ intent.setEphemeral(isEphemeral);
+ intent.setVisibleToEphemeral(visibleToEphemeral);
s.intents.add(intent);
} else if (parser.getName().equals("meta-data")) {
@@ -4755,9 +4795,9 @@
private static final String ANDROID_RESOURCES
= "http://schemas.android.com/apk/res/android";
- private boolean parseIntent(Resources res, XmlResourceParser parser,
- boolean allowGlobs, boolean allowAutoVerify, IntentInfo outInfo, String[] outError)
- throws XmlPullParserException, IOException {
+ private boolean parseIntent(Resources res, XmlResourceParser parser, boolean allowGlobs,
+ boolean allowAutoVerify, IntentInfo outInfo, String[] outError)
+ throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestIntentFilter);
diff --git a/core/java/android/content/pm/ProviderInfo.java b/core/java/android/content/pm/ProviderInfo.java
index 7e7b32f..8c21563 100644
--- a/core/java/android/content/pm/ProviderInfo.java
+++ b/core/java/android/content/pm/ProviderInfo.java
@@ -76,6 +76,12 @@
public int initOrder = 0;
/**
+ * Bit in {@link #flags} indicating if the provider is visible to ephemeral applications.
+ * @hide
+ */
+ public static final int FLAG_VISIBLE_TO_EPHEMERAL = 0x100000;
+
+ /**
* Bit in {@link #flags}: If set, a single instance of the provider will
* run for all users on the device. Set from the
* {@link android.R.attr#singleUser} attribute.
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 6bd285a..f0766be 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -56,6 +56,12 @@
public static final int FLAG_EXTERNAL_SERVICE = 0x0004;
/**
+ * Bit in {@link #flags} indicating if the service is visible to ephemeral applications.
+ * @hide
+ */
+ public static final int FLAG_VISIBLE_TO_EPHEMERAL = 0x100000;
+
+ /**
* Bit in {@link #flags}: If set, a single instance of the service will
* run for all users on the device. Set from the
* {@link android.R.attr#singleUser} attribute.
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 967c4ad..c91f0a5 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1209,6 +1209,10 @@
-->
<attr name="autoVerify" format="boolean" />
+ <!-- Specify whether a component should be visible to ephemeral apps.
+ -->
+ <attr name="visibleToEphemeral" format="boolean" />
+
<!-- An XML resource with the application's Network Security Config. -->
<attr name="networkSecurityConfig" format="reference" />
@@ -1731,6 +1735,7 @@
<attr name="exported" />
<attr name="singleUser" />
<attr name="directBootAware" />
+ <attr name="visibleToEphemeral" />
</declare-styleable>
<!-- Attributes that can be supplied in an AndroidManifest.xml
@@ -1820,6 +1825,7 @@
client to bind to the service as if it were running it its own package. The service
must also be {@link android.R.attr#exported} if this flag is set. -->
<attr name="externalService" format="boolean" />
+ <attr name="visibleToEphemeral" />
</declare-styleable>
<!-- The <code>receiver</code> tag declares an
@@ -1940,7 +1946,8 @@
<!-- @hide This activity is a launcher which should always show up on the top of others.
This attribute is ignored if the activity isn't a launcher. -->
<attr name="onTopLauncher" format="boolean" />
- <attr name="rotationAnimation"/>
+ <attr name="rotationAnimation" />
+ <attr name="visibleToEphemeral" />
</declare-styleable>
<!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index deeaf35..37c4fd1 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2775,6 +2775,7 @@
<public name="layout_marginVertical" />
<public name="paddingHorizontal" />
<public name="paddingVertical" />
+ <public name="visibleToEphemeral" />
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index 83d374c..14abb53 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -350,8 +350,8 @@
return Collections.unmodifiableSet(mFilters);
}
- public List<R> queryIntentFromList(Intent intent, String resolvedType,
- boolean defaultOnly, ArrayList<F[]> listCut, int userId) {
+ public List<R> queryIntentFromList(Intent intent, String resolvedType, boolean defaultOnly,
+ boolean visibleToEphemeral, boolean isEphemeral, ArrayList<F[]> listCut, int userId) {
ArrayList<R> resultList = new ArrayList<R>();
final boolean debug = localLOGV ||
@@ -361,8 +361,8 @@
final String scheme = intent.getScheme();
int N = listCut.size();
for (int i = 0; i < N; ++i) {
- buildResolveList(intent, categories, debug, defaultOnly,
- resolvedType, scheme, listCut.get(i), resultList, userId);
+ buildResolveList(intent, categories, debug, defaultOnly, visibleToEphemeral,
+ isEphemeral, resolvedType, scheme, listCut.get(i), resultList, userId);
}
filterResults(resultList);
sortResults(resultList);
@@ -370,7 +370,7 @@
}
public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly,
- int userId) {
+ boolean visibleToEphemeral, boolean isEphemeral, int userId) {
String scheme = intent.getScheme();
ArrayList<R> finalList = new ArrayList<R>();
@@ -443,20 +443,20 @@
FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
if (firstTypeCut != null) {
- buildResolveList(intent, categories, debug, defaultOnly,
- resolvedType, scheme, firstTypeCut, finalList, userId);
+ buildResolveList(intent, categories, debug, defaultOnly, visibleToEphemeral,
+ isEphemeral, resolvedType, scheme, firstTypeCut, finalList, userId);
}
if (secondTypeCut != null) {
- buildResolveList(intent, categories, debug, defaultOnly,
- resolvedType, scheme, secondTypeCut, finalList, userId);
+ buildResolveList(intent, categories, debug, defaultOnly, visibleToEphemeral,
+ isEphemeral, resolvedType, scheme, secondTypeCut, finalList, userId);
}
if (thirdTypeCut != null) {
- buildResolveList(intent, categories, debug, defaultOnly,
- resolvedType, scheme, thirdTypeCut, finalList, userId);
+ buildResolveList(intent, categories, debug, defaultOnly, visibleToEphemeral,
+ isEphemeral, resolvedType, scheme, thirdTypeCut, finalList, userId);
}
if (schemeCut != null) {
- buildResolveList(intent, categories, debug, defaultOnly,
- resolvedType, scheme, schemeCut, finalList, userId);
+ buildResolveList(intent, categories, debug, defaultOnly, visibleToEphemeral,
+ isEphemeral, resolvedType, scheme, schemeCut, finalList, userId);
}
filterResults(finalList);
sortResults(finalList);
@@ -694,7 +694,7 @@
}
private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories,
- boolean debug, boolean defaultOnly,
+ boolean debug, boolean defaultOnly, boolean visibleToEphemeral, boolean isEphemeral,
String resolvedType, String scheme, F[] src, List<R> dest, int userId) {
final String action = intent.getAction();
final Uri data = intent.getData();
@@ -735,6 +735,15 @@
continue;
}
+ // throw out filters that aren't visible to ephemeral apps
+ if (visibleToEphemeral && !filter.isVisibleToEphemeral()) {
+ continue;
+ }
+ // throw out ephemeral filters if we're not explicitly requesting them
+ if (!isEphemeral && filter.isEphemeral()) {
+ continue;
+ }
+
// Are we verified ?
if (filter.getAutoVerify()) {
if (localVerificationLOGV || debug) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e6e4b2d..271e1ab 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18363,7 +18363,8 @@
}
List<BroadcastFilter> registeredReceiversForUser =
mReceiverResolver.queryIntent(intent,
- resolvedType, false, users[i]);
+ resolvedType, false, false /*visibleToEphemeral*/,
+ false /*isEphemeral*/, users[i]);
if (registeredReceivers == null) {
registeredReceivers = registeredReceiversForUser;
} else if (registeredReceiversForUser != null) {
@@ -18372,7 +18373,8 @@
}
} else {
registeredReceivers = mReceiverResolver.queryIntent(intent,
- resolvedType, false, userId);
+ resolvedType, false, false /*visibleToEphemeral*/,
+ false /*isEphemeral*/, userId);
}
}
diff --git a/services/core/java/com/android/server/firewall/IntentFirewall.java b/services/core/java/com/android/server/firewall/IntentFirewall.java
index 7e19c66..19eed35 100644
--- a/services/core/java/com/android/server/firewall/IntentFirewall.java
+++ b/services/core/java/com/android/server/firewall/IntentFirewall.java
@@ -151,7 +151,8 @@
// For the first pass, find all the rules that have at least one intent-filter or
// component-filter that matches this intent
List<Rule> candidateRules;
- candidateRules = resolver.queryIntent(intent, resolvedType, false, 0);
+ candidateRules = resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/,
+ false /*visibleToEphemeral*/, false /*isEphemeral*/, 0);
if (candidateRules == null) {
candidateRules = new ArrayList<Rule>();
}
diff --git a/services/core/java/com/android/server/pm/EphemeralResolver.java b/services/core/java/com/android/server/pm/EphemeralResolver.java
index 3ce5007..d735e72 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolver.java
+++ b/services/core/java/com/android/server/pm/EphemeralResolver.java
@@ -227,8 +227,9 @@
ephemeralResolver.addFilter(intentInfo);
}
}
- List<EphemeralResponse> matchedResolveInfoList = ephemeralResolver
- .queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);
+ List<EphemeralResponse> matchedResolveInfoList = ephemeralResolver.queryIntent(
+ intent, resolvedType, false /*defaultOnly*/, false /*visibleToEphemeral*/,
+ false /*isEphemeral*/, userId);
if (!matchedResolveInfoList.isEmpty()) {
return matchedResolveInfoList.get(0);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d8f65c8..bd3eee2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3640,6 +3640,11 @@
if (mSafeMode) {
flags |= PackageManager.MATCH_SYSTEM_ONLY;
}
+ final String ephemeralPkgName = getEphemeralPackageName(Binder.getCallingUid());
+ if (ephemeralPkgName != null) {
+ flags |= PackageManager.MATCH_VISIBLE_TO_EPHEMERAL_ONLY;
+ flags |= PackageManager.MATCH_EPHEMERAL;
+ }
return updateFlagsForComponent(flags, userId, cookie);
}
@@ -5213,7 +5218,9 @@
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for presistent preferred activities...");
List<PersistentPreferredActivity> pprefs = ppir != null
? ppir.queryIntent(intent, resolvedType,
- (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId)
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
+ (flags & PackageManager.MATCH_VISIBLE_TO_EPHEMERAL_ONLY) != 0,
+ (flags & PackageManager.MATCH_EPHEMERAL) != 0, userId)
: null;
if (pprefs != null && pprefs.size() > 0) {
final int M = pprefs.size();
@@ -5288,7 +5295,9 @@
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for preferred activities...");
List<PreferredActivity> prefs = pir != null
? pir.queryIntent(intent, resolvedType,
- (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId)
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
+ (flags & PackageManager.MATCH_VISIBLE_TO_EPHEMERAL_ONLY) != 0,
+ (flags & PackageManager.MATCH_EPHEMERAL) != 0, userId)
: null;
if (prefs != null && prefs.size() > 0) {
boolean changed = false;
@@ -5459,7 +5468,8 @@
String resolvedType, int userId) {
CrossProfileIntentResolver resolver = mSettings.mCrossProfileIntentResolvers.get(userId);
if (resolver != null) {
- return resolver.queryIntent(intent, resolvedType, false, userId);
+ return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/,
+ false /*visibleToEphemeral*/, false /*isEphemeral*/, userId);
}
return null;
}
@@ -5477,9 +5487,26 @@
}
}
+ /**
+ * Returns the package name of the calling Uid if it's an ephemeral app. If it isn't
+ * ephemeral, returns {@code null}.
+ */
+ private String getEphemeralPackageName(int callingUid) {
+ final int appId = UserHandle.getAppId(callingUid);
+ synchronized (mPackages) {
+ final Object obj = mSettings.getUserIdLPr(appId);
+ if (obj instanceof PackageSetting) {
+ final PackageSetting ps = (PackageSetting) obj;
+ return ps.pkg.applicationInfo.isEphemeralApp() ? ps.pkg.packageName : null;
+ }
+ }
+ return null;
+ }
+
private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
+ final String ephemeralPkgName = getEphemeralPackageName(Binder.getCallingUid());
flags = updateFlagsForResolve(flags, userId, intent);
enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */,
@@ -5506,7 +5533,6 @@
// reader
boolean sortResult = false;
boolean addEphemeral = false;
- boolean matchEphemeralPackage = false;
List<ResolveInfo> result;
final String pkgName = intent.getPackage();
synchronized (mPackages) {
@@ -5519,7 +5545,8 @@
if (xpResolveInfo != null) {
List<ResolveInfo> xpResult = new ArrayList<ResolveInfo>(1);
xpResult.add(xpResolveInfo);
- return filterIfNotSystemUser(xpResult, userId);
+ return filterForEphemeral(
+ filterIfNotSystemUser(xpResult, userId), ephemeralPkgName);
}
// Check for results in the current profile.
@@ -5559,13 +5586,13 @@
// And we are not going to add emphemeral app, so we can return the
// result straight away.
result.add(xpDomainInfo.resolveInfo);
- return result;
+ return filterForEphemeral(result, ephemeralPkgName);
}
} else if (result.size() <= 1 && !addEphemeral) {
// No result in parent user and <= 1 result in current profile, and we
// are not going to add emphemeral app, so we can return the result without
// further processing.
- return result;
+ return filterForEphemeral(result, ephemeralPkgName);
}
// We have more than one candidate (combining results from current and parent
// profile), so we need filtering and sorting.
@@ -5585,7 +5612,6 @@
// were no installed results, so, try to find an ephemeral result
addEphemeral = isEphemeralAllowed(
intent, null /*result*/, userId, true /*skipPackageCheck*/);
- matchEphemeralPackage = true;
result = new ArrayList<ResolveInfo>();
}
}
@@ -5618,7 +5644,7 @@
if (sortResult) {
Collections.sort(result, mResolvePrioritySorter);
}
- return result;
+ return filterForEphemeral(result, ephemeralPkgName);
}
private static class CrossProfileDomainInfo {
@@ -5717,6 +5743,38 @@
}
/**
+ * Filters out ephemeral activities.
+ * <p>When resolving for an ephemeral app, only activities that 1) are defined in the
+ * ephemeral app or 2) marked with {@code visibleToEphemeral} are returned.
+ *
+ * @param resolveInfos The pre-filtered list of resolved activities
+ * @param ephemeralPkgName The ephemeral package name. If {@code null}, no filtering
+ * is performed.
+ * @return A filtered list of resolved activities.
+ */
+ private List<ResolveInfo> filterForEphemeral(List<ResolveInfo> resolveInfos,
+ String ephemeralPkgName) {
+ if (ephemeralPkgName == null) {
+ return resolveInfos;
+ }
+ for (int i = resolveInfos.size() - 1; i >= 0; i--) {
+ ResolveInfo info = resolveInfos.get(i);
+ final boolean isEphemeralApp = info.activityInfo.applicationInfo.isEphemeralApp();
+ // allow activities that are defined in the provided package
+ if (isEphemeralApp && ephemeralPkgName.equals(info.activityInfo.packageName)) {
+ continue;
+ }
+ // allow activities that have been explicitly exposed to ephemeral apps
+ if (!isEphemeralApp
+ && ((info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL) != 0)) {
+ continue;
+ }
+ resolveInfos.remove(i);
+ }
+ return resolveInfos;
+ }
+
+ /**
* @param resolveInfos list of resolve infos in descending priority order
* @return if the list contains a resolve info with non-negative priority
*/
@@ -10741,10 +10799,13 @@
final class ActivityIntentResolver
extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> {
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
- boolean defaultOnly, int userId) {
+ boolean defaultOnly, boolean visibleToEphemeral, boolean isEphemeral, int userId) {
if (!sUserManager.exists(userId)) return null;
- mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
- return super.queryIntent(intent, resolvedType, defaultOnly, userId);
+ mFlags = (defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0)
+ | (visibleToEphemeral ? PackageManager.MATCH_VISIBLE_TO_EPHEMERAL_ONLY : 0)
+ | (isEphemeral ? PackageManager.MATCH_EPHEMERAL : 0);
+ return super.queryIntent(intent, resolvedType, defaultOnly, visibleToEphemeral,
+ isEphemeral, userId);
}
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags,
@@ -10752,7 +10813,9 @@
if (!sUserManager.exists(userId)) return null;
mFlags = flags;
return super.queryIntent(intent, resolvedType,
- (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId);
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
+ (flags & PackageManager.MATCH_VISIBLE_TO_EPHEMERAL_ONLY) != 0,
+ (flags & PackageManager.MATCH_EPHEMERAL) != 0, userId);
}
public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
@@ -10762,7 +10825,10 @@
return null;
}
mFlags = flags;
- final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0;
+ final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
+ final boolean vislbleToEphemeral =
+ (flags & PackageManager.MATCH_VISIBLE_TO_EPHEMERAL_ONLY) != 0;
+ final boolean isEphemeral = (flags & PackageManager.MATCH_EPHEMERAL) != 0;
final int N = packageActivities.size();
ArrayList<PackageParser.ActivityIntentInfo[]> listCut =
new ArrayList<PackageParser.ActivityIntentInfo[]>(N);
@@ -10777,7 +10843,8 @@
listCut.add(array);
}
}
- return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
+ return super.queryIntentFromList(intent, resolvedType, defaultOnly,
+ vislbleToEphemeral, isEphemeral, listCut, userId);
}
/**
@@ -11262,9 +11329,10 @@
private final class ServiceIntentResolver
extends IntentResolver<PackageParser.ServiceIntentInfo, ResolveInfo> {
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
- boolean defaultOnly, int userId) {
+ boolean defaultOnly, boolean visibleToEphemeral, boolean isEphemeral, int userId) {
mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
- return super.queryIntent(intent, resolvedType, defaultOnly, userId);
+ return super.queryIntent(intent, resolvedType, defaultOnly, visibleToEphemeral,
+ isEphemeral, userId);
}
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags,
@@ -11272,7 +11340,9 @@
if (!sUserManager.exists(userId)) return null;
mFlags = flags;
return super.queryIntent(intent, resolvedType,
- (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId);
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
+ (flags & PackageManager.MATCH_VISIBLE_TO_EPHEMERAL_ONLY) != 0,
+ (flags & PackageManager.MATCH_EPHEMERAL) != 0, userId);
}
public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
@@ -11283,6 +11353,9 @@
}
mFlags = flags;
final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0;
+ final boolean vislbleToEphemeral =
+ (flags&PackageManager.MATCH_VISIBLE_TO_EPHEMERAL_ONLY) != 0;
+ final boolean isEphemeral = (flags&PackageManager.MATCH_EPHEMERAL) != 0;
final int N = packageServices.size();
ArrayList<PackageParser.ServiceIntentInfo[]> listCut =
new ArrayList<PackageParser.ServiceIntentInfo[]>(N);
@@ -11297,7 +11370,8 @@
listCut.add(array);
}
}
- return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
+ return super.queryIntentFromList(intent, resolvedType, defaultOnly,
+ vislbleToEphemeral, isEphemeral, listCut, userId);
}
public final void addService(PackageParser.Service s) {
@@ -11467,14 +11541,15 @@
private final ArrayMap<ComponentName, PackageParser.Service> mServices
= new ArrayMap<ComponentName, PackageParser.Service>();
private int mFlags;
- };
+ }
private final class ProviderIntentResolver
extends IntentResolver<PackageParser.ProviderIntentInfo, ResolveInfo> {
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
- boolean defaultOnly, int userId) {
+ boolean defaultOnly, boolean visibleToEphemeral, boolean isEphemeral, int userId) {
mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
- return super.queryIntent(intent, resolvedType, defaultOnly, userId);
+ return super.queryIntent(intent, resolvedType, defaultOnly, visibleToEphemeral,
+ isEphemeral, userId);
}
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags,
@@ -11483,7 +11558,9 @@
return null;
mFlags = flags;
return super.queryIntent(intent, resolvedType,
- (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId);
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
+ (flags & PackageManager.MATCH_VISIBLE_TO_EPHEMERAL_ONLY) != 0,
+ (flags & PackageManager.MATCH_EPHEMERAL) != 0, userId);
}
public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
@@ -11495,6 +11572,9 @@
}
mFlags = flags;
final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
+ final boolean isEphemeral = (flags&PackageManager.MATCH_EPHEMERAL) != 0;
+ final boolean vislbleToEphemeral =
+ (flags&PackageManager.MATCH_VISIBLE_TO_EPHEMERAL_ONLY) != 0;
final int N = packageProviders.size();
ArrayList<PackageParser.ProviderIntentInfo[]> listCut =
new ArrayList<PackageParser.ProviderIntentInfo[]>(N);
@@ -11509,7 +11589,8 @@
listCut.add(array);
}
}
- return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
+ return super.queryIntentFromList(intent, resolvedType, defaultOnly,
+ vislbleToEphemeral, isEphemeral, listCut, userId);
}
public final void addProvider(PackageParser.Provider p) {