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;
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;
// 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*/);
}
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) {
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;
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);
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;
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) {
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;
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) {
}
// 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);
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);
Collections.singletonList(xpResolveInfo), userId).size() > 0;
if (isVisibleToUser) {
result.add(xpResolveInfo);
- Collections.sort(result, mResolvePrioritySorter);
+ sortResult = true;
}
}
if (hasWebURI(intent)) {
// 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 {
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);