summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Todd Kennedy <toddke@google.com> 2016-06-28 14:09:55 -0700
committer Todd Kennedy <toddke@google.com> 2016-07-21 14:55:23 -0700
commit8e2d9d1d9050e93b15c54e992698325c7d4aa57c (patch)
treec30629c86b02df7e28d81c05c3be242029c37a8d
parent0ce1875e0c3ad5541e1e2151e41197c7e363fe40 (diff)
Implement new ephemeral resolver design
In the new design, the ephemeral installer can be returned from queryIntentActivities which means any intent resolution could potentially return the installer. Additionally, the new design calls for a platform defined broadcast receiver that receives the status from the ephemeral installer. This receiver then starts the final intent -- either to launch the ephemeral application or to launch the fallback. For more detail, see go/ephemeral-design Change-Id: I6644bbb4f180d2d22c63af04b9857577516344a9
-rw-r--r--core/java/android/content/Intent.java8
-rw-r--r--core/java/android/content/pm/ResolveInfo.java6
-rw-r--r--services/core/java/com/android/server/am/ActivityStarter.java90
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java138
4 files changed, 155 insertions, 87 deletions
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 43a8a822255f..d53773968a89 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4314,6 +4314,14 @@ public class Intent implements Parcelable, Cloneable {
public static final int FLAG_DEBUG_TRIAGED_MISSING = 0x00000100;
/**
+ * Internal flag used to indicate ephemeral applications should not be
+ * considered when resolving the intent.
+ *
+ * @hide
+ */
+ public static final int FLAG_IGNORE_EPHEMERAL = 0x00000200;
+
+ /**
* If set, the new activity is not kept in the history stack. As soon as
* the user navigates away from it, the activity is finished. This may also
* be set with the {@link android.R.styleable#AndroidManifestActivity_noHistory
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index c9be6edab424..b5df4d75a238 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -68,12 +68,6 @@ public class ResolveInfo implements Parcelable {
public EphemeralResolveInfo ephemeralResolveInfo;
/**
- * A ResolveInfo that points at the ephemeral installer.
- * @hide
- */
- public ResolveInfo ephemeralInstaller;
-
- /**
* The IntentFilter that was matched for this ResolveInfo.
*/
public IntentFilter filter;
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 399ca9eed651..2ee6de2e8c38 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -103,6 +103,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PowerManagerInternal;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -132,6 +133,9 @@ class ActivityStarter {
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
+ // TODO b/30204367 remove when the platform fully supports ephemeral applications
+ private static final boolean USE_DEFAULT_EPHEMERAL_LAUNCHER = false;
+
private final ActivityManagerService mService;
private final ActivityStackSupervisor mSupervisor;
private ActivityStartInterceptor mInterceptor;
@@ -456,39 +460,13 @@ class ActivityStarter {
// starts either the intent we resolved here [on install error] or the ephemeral
// app [on install success].
if (rInfo != null && rInfo.ephemeralResolveInfo != null) {
- // Create a pending intent to start the intent resolved here.
- final IIntentSender failureTarget = mService.getIntentSenderLocked(
- ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
- Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
- new String[]{ resolvedType },
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
- | PendingIntent.FLAG_IMMUTABLE, null);
-
- // Create a pending intent to start the ephemeral application; force it to be
- // directed to the ephemeral package.
- ephemeralIntent.setPackage(rInfo.ephemeralResolveInfo.getPackageName());
- final IIntentSender ephemeralTarget = mService.getIntentSenderLocked(
- ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
- Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ ephemeralIntent },
- new String[]{ resolvedType },
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
- | PendingIntent.FLAG_IMMUTABLE, null);
-
- int flags = intent.getFlags();
- intent = new Intent();
- intent.setFlags(flags
- | Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME,
- rInfo.ephemeralResolveInfo.getPackageName());
- intent.putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, new IntentSender(failureTarget));
- intent.putExtra(Intent.EXTRA_EPHEMERAL_SUCCESS, new IntentSender(ephemeralTarget));
-
+ intent = buildEphemeralInstallerIntent(intent, ephemeralIntent,
+ rInfo.ephemeralResolveInfo.getPackageName(), callingPackage, resolvedType,
+ userId);
resolvedType = null;
callingUid = realCallingUid;
callingPid = realCallingPid;
- rInfo = rInfo.ephemeralInstaller;
aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
}
@@ -543,6 +521,60 @@ class ActivityStarter {
return err;
}
+ /**
+ * Builds and returns an intent to launch the ephemeral installer.
+ */
+ private Intent buildEphemeralInstallerIntent(Intent launchIntent, Intent origIntent,
+ String ephemeralPackage, String callingPackage, String resolvedType, int userId) {
+ final Intent nonEphemeralIntent = new Intent(origIntent);
+ nonEphemeralIntent.setFlags(nonEphemeralIntent.getFlags() | Intent.FLAG_IGNORE_EPHEMERAL);
+ // Intent that is launched if the ephemeral package couldn't be installed
+ // for any reason.
+ final IIntentSender failureIntentTarget = mService.getIntentSenderLocked(
+ ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+ Binder.getCallingUid(), userId, null /*token*/, null /*resultWho*/, 1,
+ new Intent[]{ nonEphemeralIntent }, new String[]{ resolvedType },
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
+ | PendingIntent.FLAG_IMMUTABLE, null /*bOptions*/);
+
+ final Intent ephemeralIntent;
+ if (USE_DEFAULT_EPHEMERAL_LAUNCHER) {
+ // Force the intent to be directed to the ephemeral package
+ ephemeralIntent = new Intent(origIntent);
+ ephemeralIntent.setPackage(ephemeralPackage);
+ } else {
+ // Success intent goes back to the installer
+ // TODO; do we need any extras for the installer?
+ ephemeralIntent = new Intent(launchIntent);
+ ephemeralIntent.setData(null);
+ }
+
+ // Intent that is eventually launched if the ephemeral package was
+ // installed successfully. This will actually be launched by a platform
+ // broadcast receiver.
+ final IIntentSender successIntentTarget = mService.getIntentSenderLocked(
+ ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+ Binder.getCallingUid(), userId, null /*token*/, null /*resultWho*/, 0,
+ new Intent[]{ ephemeralIntent }, new String[]{ resolvedType },
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
+ | PendingIntent.FLAG_IMMUTABLE, null /*bOptions*/);
+
+ // Finally build the actual intent to launch the ephemeral installer
+ int flags = launchIntent.getFlags();
+ final Intent intent = new Intent();
+ intent.setFlags(flags
+ | Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_ACTIVITY_NO_HISTORY
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, ephemeralPackage);
+ intent.putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, new IntentSender(failureIntentTarget));
+ intent.putExtra(Intent.EXTRA_EPHEMERAL_SUCCESS, new IntentSender(successIntentTarget));
+ // TODO: Remove when the platform has fully implemented ephemeral apps
+ intent.setData(origIntent.getData());
+ return intent;
+ }
+
void postStartActivityUncheckedProcessing(
ActivityRecord r, int result, int prevFocusedStackId, ActivityRecord sourceRecord,
ActivityStack targetStack) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index de0515ac0bd4..3d1d76eb2d70 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -179,6 +179,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
+import android.os.PatternMatcher;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -4727,20 +4728,6 @@ public class PackageManagerService extends IPackageManager.Stub {
final ResolveInfo bestChoice =
chooseBestActivity(intent, resolvedType, flags, query, userId);
-
- if (isEphemeralAllowed(intent, query, userId)) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
- final EphemeralResolveInfo ai =
- getEphemeralResolveInfo(intent, resolvedType, userId);
- if (ai != null) {
- if (DEBUG_EPHEMERAL) {
- Slog.v(TAG, "Returning an EphemeralResolveInfo");
- }
- bestChoice.ephemeralInstaller = mEphemeralInstallerInfo;
- bestChoice.ephemeralResolveInfo = ai;
- }
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
return bestChoice;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -4781,9 +4768,9 @@ public class PackageManagerService extends IPackageManager.Stub {
false, false, false, userId);
}
-
private boolean isEphemeralAllowed(
- Intent intent, List<ResolveInfo> resolvedActivites, int userId) {
+ Intent intent, List<ResolveInfo> resolvedActivities, int userId,
+ boolean skipPackageCheck) {
// Short circuit and return early if possible.
if (DISABLE_EPHEMERAL_APPS) {
return false;
@@ -4798,18 +4785,21 @@ public class PackageManagerService extends IPackageManager.Stub {
if (intent.getComponent() != null) {
return false;
}
- if (intent.getPackage() != null) {
+ if ((intent.getFlags() & Intent.FLAG_IGNORE_EPHEMERAL) != 0) {
+ return false;
+ }
+ if (!skipPackageCheck && intent.getPackage() != null) {
return false;
}
final boolean isWebUri = hasWebURI(intent);
- if (!isWebUri) {
+ if (!isWebUri || intent.getData().getHost() == null) {
return false;
}
// Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
synchronized (mPackages) {
- final int count = resolvedActivites.size();
+ final int count = (resolvedActivities == null ? 0 : resolvedActivities.size());
for (int n = 0; n < count; n++) {
- ResolveInfo info = resolvedActivites.get(n);
+ ResolveInfo info = resolvedActivities.get(n);
String packageName = info.activityInfo.packageName;
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
@@ -4831,19 +4821,19 @@ public class PackageManagerService extends IPackageManager.Stub {
return true;
}
- private EphemeralResolveInfo getEphemeralResolveInfo(Intent intent, String resolvedType,
- int userId) {
- final int ephemeralPrefixMask = Global.getInt(mContext.getContentResolver(),
+ private static EphemeralResolveInfo getEphemeralResolveInfo(
+ Context context, EphemeralResolverConnection resolverConnection, Intent intent,
+ String resolvedType, int userId, String packageName) {
+ final int ephemeralPrefixMask = Global.getInt(context.getContentResolver(),
Global.EPHEMERAL_HASH_PREFIX_MASK, DEFAULT_EPHEMERAL_HASH_PREFIX_MASK);
- final int ephemeralPrefixCount = Global.getInt(mContext.getContentResolver(),
+ final int ephemeralPrefixCount = Global.getInt(context.getContentResolver(),
Global.EPHEMERAL_HASH_PREFIX_COUNT, DEFAULT_EPHEMERAL_HASH_PREFIX_COUNT);
final EphemeralDigest digest = new EphemeralDigest(intent.getData(), ephemeralPrefixMask,
ephemeralPrefixCount);
final int[] shaPrefix = digest.getDigestPrefix();
final byte[][] digestBytes = digest.getDigestBytes();
final List<EphemeralResolveInfo> ephemeralResolveInfoList =
- mEphemeralResolverConnection.getEphemeralResolveInfoList(
- shaPrefix, ephemeralPrefixMask);
+ resolverConnection.getEphemeralResolveInfoList(shaPrefix, ephemeralPrefixMask);
if (ephemeralResolveInfoList == null || ephemeralResolveInfoList.size() == 0) {
// No hash prefix match; there are no ephemeral apps for this domain.
return null;
@@ -4860,6 +4850,10 @@ public class PackageManagerService extends IPackageManager.Stub {
if (filters.isEmpty()) {
continue;
}
+ if (packageName != null
+ && !packageName.equals(ephemeralApplication.getPackageName())) {
+ continue;
+ }
// We have a domain match; resolve the filters to see if anything matches.
final EphemeralIntentResolver ephemeralResolver = new EphemeralIntentResolver();
for (int j = filters.size() - 1; j >= 0; --j) {
@@ -5263,8 +5257,12 @@ public class PackageManagerService extends IPackageManager.Stub {
}
// reader
+ boolean sortResult = false;
+ boolean addEphemeral = false;
+ boolean matchEphemeralPackage = false;
+ List<ResolveInfo> result;
+ final String pkgName = intent.getPackage();
synchronized (mPackages) {
- final String pkgName = intent.getPackage();
if (pkgName == null) {
List<CrossProfileIntentFilter> matchingFilters =
getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
@@ -5272,15 +5270,16 @@ public class PackageManagerService extends IPackageManager.Stub {
ResolveInfo xpResolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent,
resolvedType, flags, userId);
if (xpResolveInfo != null) {
- List<ResolveInfo> result = new ArrayList<ResolveInfo>(1);
- result.add(xpResolveInfo);
- return filterIfNotSystemUser(result, userId);
+ List<ResolveInfo> xpResult = new ArrayList<ResolveInfo>(1);
+ xpResult.add(xpResolveInfo);
+ return filterIfNotSystemUser(xpResult, userId);
}
// Check for results in the current profile.
- List<ResolveInfo> result = mActivities.queryIntent(
- intent, resolvedType, flags, userId);
- result = filterIfNotSystemUser(result, userId);
+ result = filterIfNotSystemUser(mActivities.queryIntent(
+ intent, resolvedType, flags, userId), userId);
+ addEphemeral =
+ isEphemeralAllowed(intent, result, userId, false /*skipPackageCheck*/);
// Check for cross profile results.
boolean hasNonNegativePriorityResult = hasNonNegativePriority(result);
@@ -5292,7 +5291,7 @@ public class PackageManagerService extends IPackageManager.Stub {
Collections.singletonList(xpResolveInfo), userId).size() > 0;
if (isVisibleToUser) {
result.add(xpResolveInfo);
- Collections.sort(result, mResolvePrioritySorter);
+ sortResult = true;
}
}
if (hasWebURI(intent)) {
@@ -5308,28 +5307,61 @@ public class PackageManagerService extends IPackageManager.Stub {
// in the result.
result.remove(xpResolveInfo);
}
- if (result.size() == 0) {
+ if (result.size() == 0 && !addEphemeral) {
result.add(xpDomainInfo.resolveInfo);
return result;
}
- } else if (result.size() <= 1) {
- return result;
}
- result = filterCandidatesWithDomainPreferredActivitiesLPr(intent, flags, result,
- xpDomainInfo, userId);
- Collections.sort(result, mResolvePrioritySorter);
+ if (result.size() > 1 || addEphemeral) {
+ result = filterCandidatesWithDomainPreferredActivitiesLPr(
+ intent, flags, result, xpDomainInfo, userId);
+ sortResult = true;
+ }
+ }
+ } else {
+ final PackageParser.Package pkg = mPackages.get(pkgName);
+ if (pkg != null) {
+ result = filterIfNotSystemUser(
+ mActivities.queryIntentForPackage(
+ intent, resolvedType, flags, pkg.activities, userId),
+ userId);
+ } else {
+ // the caller wants to resolve for a particular package; however, there
+ // 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>();
}
- return result;
}
- final PackageParser.Package pkg = mPackages.get(pkgName);
- if (pkg != null) {
- return filterIfNotSystemUser(
- mActivities.queryIntentForPackage(
- intent, resolvedType, flags, pkg.activities, userId),
- userId);
+ }
+ if (addEphemeral) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
+ final EphemeralResolveInfo ai = getEphemeralResolveInfo(
+ mContext, mEphemeralResolverConnection, intent, resolvedType, userId,
+ matchEphemeralPackage ? pkgName : null);
+ if (ai != null) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
+ }
+ final ResolveInfo ephemeralInstaller = new ResolveInfo(mEphemeralInstallerInfo);
+ ephemeralInstaller.ephemeralResolveInfo = ai;
+ // make sure this resolver is the default
+ ephemeralInstaller.isDefault = true;
+ ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
+ | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
+ // add a non-generic filter
+ ephemeralInstaller.filter = new IntentFilter(intent.getAction());
+ ephemeralInstaller.filter.addDataPath(
+ intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL);
+ result.add(ephemeralInstaller);
}
- return new ArrayList<ResolveInfo>();
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ if (sortResult) {
+ Collections.sort(result, mResolvePrioritySorter);
}
+ return result;
}
private static class CrossProfileDomainInfo {
@@ -9194,15 +9226,17 @@ public class PackageManagerService extends IPackageManager.Stub {
mEphemeralInstallerActivity.packageName = pkg.applicationInfo.packageName;
mEphemeralInstallerActivity.processName = pkg.applicationInfo.packageName;
mEphemeralInstallerActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
- mEphemeralInstallerActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS |
- ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
+ mEphemeralInstallerActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS
+ | ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
mEphemeralInstallerActivity.theme = 0;
mEphemeralInstallerActivity.exported = true;
mEphemeralInstallerActivity.enabled = true;
mEphemeralInstallerInfo.activityInfo = mEphemeralInstallerActivity;
mEphemeralInstallerInfo.priority = 0;
- mEphemeralInstallerInfo.preferredOrder = 0;
- mEphemeralInstallerInfo.match = 0;
+ mEphemeralInstallerInfo.preferredOrder = 1;
+ mEphemeralInstallerInfo.isDefault = true;
+ mEphemeralInstallerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
+ | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
if (DEBUG_EPHEMERAL) {
Slog.d(TAG, "Set ephemeral installer activity: " + mEphemeralInstallerComponent);