Implement new ephemeral resolver design

am: fea1b776e9

Change-Id: Ied8a583de6247a5d826805dfbb5e8edbecdcdb27
This commit is contained in:
Todd Kennedy
2016-08-26 20:32:45 +00:00
committed by android-build-merger
4 changed files with 156 additions and 88 deletions

View File

@@ -4313,6 +4313,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

View File

@@ -67,12 +67,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.
*/

View File

@@ -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) {

View File

@@ -177,6 +177,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;
@@ -4725,20 +4726,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);
@@ -4779,9 +4766,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;
@@ -4796,18 +4783,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) {
@@ -4829,19 +4819,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;
@@ -4858,6 +4848,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) {
@@ -5261,8 +5255,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);
@@ -5270,15 +5268,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);
@@ -5290,7 +5289,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)) {
@@ -5306,28 +5305,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);
}
return new ArrayList<ResolveInfo>();
}
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);
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
if (sortResult) {
Collections.sort(result, mResolvePrioritySorter);
}
return result;
}
private static class CrossProfileDomainInfo {
@@ -9205,15 +9237,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);