Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fea1b776 authored by Todd Kennedy's avatar Todd Kennedy
Browse files

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

Bug: 30805203
Bug: 30273584
Change-Id: I6644bbb4f180d2d22c63af04b9857577516344a9
(cherry picked from commit 8e2d9d1d)
parent e24a4590
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -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
+0 −6
Original line number Diff line number Diff line
@@ -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.
     */
+61 −29
Original line number Diff line number Diff line
@@ -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) {
+86 −52
Original line number Diff line number Diff line
@@ -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;
@@ -4723,20 +4724,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);
@@ -4777,9 +4764,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;
@@ -4794,18 +4781,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) {
@@ -4827,19 +4817,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;
@@ -4856,6 +4846,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) {
@@ -5259,8 +5253,12 @@ public class PackageManagerService extends IPackageManager.Stub {
        }
        // reader
        synchronized (mPackages) {
        boolean sortResult = false;
        boolean addEphemeral = false;
        boolean matchEphemeralPackage = false;
        List<ResolveInfo> result;
        final String pkgName = intent.getPackage();
        synchronized (mPackages) {
            if (pkgName == null) {
                List<CrossProfileIntentFilter> matchingFilters =
                        getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
@@ -5268,15 +5266,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);
@@ -5288,7 +5287,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)) {
@@ -5304,29 +5303,62 @@ 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;
                    }
                return result;
                }
            } else {
                final PackageParser.Package pkg = mPackages.get(pkgName);
                if (pkg != null) {
                return filterIfNotSystemUser(
                    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 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 {
        /* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */
@@ -9190,15 +9222,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);