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

Commit 47cf5f22 authored by Louis Chang's avatar Louis Chang
Browse files

Allow requesting start-activity-permission-token for an activity

The permission token was required to be requested from an existing
activity. Since the token is delegated to another activity, the
activity that requested the token had to keep alive and finish itself
until the token is revoked.

Changing the behavior to allow the caller to request the permission
token for a particular component as long as the caller is running in
System uid (which not necessarily to be an Activity).

This also prevents having an additional trampoline activity launched.

Bug: 199743918
Test: locally tested
Change-Id: If3e55032bb99d98a446e4c30220555b0fbc0c6f7
parent c2dc611a
Loading
Loading
Loading
Loading
+5 −6
Original line number Diff line number Diff line
@@ -178,17 +178,16 @@ interface IActivityTaskManager {
    Point getAppTaskThumbnailSize();
    /**
     * Only callable from the system. This token grants a temporary permission to call
     * #startActivityAsCallerWithToken. The token will time out after
     * START_AS_CALLER_TOKEN_TIMEOUT if it is not used.
     * #startActivityAsCaller. The token will time out after START_AS_CALLER_TOKEN_TIMEOUT
     * if it is not used.
     *
     * @param delegatorToken The Binder token referencing the system Activity that wants to delegate
     *        the #startActivityAsCaller to another app. The "caller" will be the caller of this
     *        activity's token, not the delegate's caller (which is probably the delegator itself).
     * @param componentName The component name of the delegated component that is allowed to
     *                      call #startActivityAsCaller with the returned token.
     *
     * @return Returns a token that can be given to a "delegate" app that may call
     *         #startActivityAsCaller
     */
    IBinder requestStartActivityPermissionToken(in IBinder delegatorToken);
    IBinder requestStartActivityPermissionToken(in ComponentName componentName);

    oneway void releaseSomeActivities(in IApplicationThread app);
    Bitmap getTaskDescriptionIcon(in String filename, int userId);
+2 −2
Original line number Diff line number Diff line
@@ -759,11 +759,11 @@ public class ChooserActivity extends ResolverActivity implements
        }

        try {
            IBinder permissionToken = ActivityTaskManager.getService()
                    .requestStartActivityPermissionToken(getActivityToken());
            Intent delegationIntent = new Intent();
            final ComponentName delegateActivity = ComponentName.unflattenFromString(
                    Resources.getSystem().getString(R.string.config_chooserActivity));
            IBinder permissionToken = ActivityTaskManager.getService()
                    .requestStartActivityPermissionToken(delegateActivity);
            delegationIntent.setComponent(delegateActivity);
            delegationIntent.putExtra(Intent.EXTRA_INTENT, getIntent());
            delegationIntent.putExtra(ActivityTaskManager.EXTRA_PERMISSION_TOKEN, permissionToken);
+2 −2
Original line number Diff line number Diff line
@@ -1437,11 +1437,11 @@ public class ResolverActivity extends Activity implements
        try {
            // TODO: Once this is a small springboard activity, it can move off the UI process
            // and we can move the request method to ActivityManagerInternal.
            IBinder permissionToken = ActivityTaskManager.getService()
                    .requestStartActivityPermissionToken(getActivityToken());
            final Intent chooserIntent = new Intent();
            final ComponentName delegateActivity = ComponentName.unflattenFromString(
                    Resources.getSystem().getString(R.string.config_chooserActivity));
            IBinder permissionToken = ActivityTaskManager.getService()
                    .requestStartActivityPermissionToken(delegateActivity);
            chooserIntent.setClassName(delegateActivity.getPackageName(),
                    delegateActivity.getClassName());
            chooserIntent.putExtra(ActivityTaskManager.EXTRA_PERMISSION_TOKEN, permissionToken);
+35 −39
Original line number Diff line number Diff line
@@ -436,9 +436,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
    private static final long START_AS_CALLER_TOKEN_EXPIRED_TIMEOUT =
            START_AS_CALLER_TOKEN_TIMEOUT_IMPL + 20 * MINUTE_IN_MILLIS;

    // Activity tokens of system activities that are delegating their call to
    // #startActivityByCaller, keyed by the permissionToken granted to the delegate.
    final HashMap<IBinder, IBinder> mStartActivitySources = new HashMap<>();
    // The component name of the delegated activities that are allowed to call
    // #startActivityAsCaller with the one-time used permission token.
    final HashMap<IBinder, ComponentName> mStartActivitySources = new HashMap<>();

    // Permission tokens that have expired, but we remember for error reporting.
    final ArrayList<IBinder> mExpiredStartAsCallerTokens = new ArrayList<>();
@@ -1512,7 +1512,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
    }

    @Override
    public IBinder requestStartActivityPermissionToken(IBinder delegatorToken) {
    public IBinder requestStartActivityPermissionToken(ComponentName componentName) {
        int callingUid = Binder.getCallingUid();
        if (UserHandle.getAppId(callingUid) != SYSTEM_UID) {
            throw new SecurityException("Only the system process can request a permission token, "
@@ -1520,7 +1520,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
        }
        IBinder permissionToken = new Binder();
        synchronized (mGlobalLock) {
            mStartActivitySources.put(permissionToken, delegatorToken);
            mStartActivitySources.put(permissionToken, componentName);
        }

        Message expireMsg = PooledLambda.obtainMessage(
@@ -1545,7 +1545,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
        // 1)  The caller is an activity that is part of the core framework, and then only when it
        //     is running as the system.
        // 2)  The caller provides a valid permissionToken.  Permission tokens are one-time use and
        //     can only be requested by a system activity, which may then delegate this call to
        //     can only be requested from system uid, which may then delegate this call to
        //     another app.
        final ActivityRecord sourceRecord;
        final int targetUid;
@@ -1556,18 +1556,26 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
            if (resultTo == null) {
                throw new SecurityException("Must be called from an activity");
            }
            final IBinder sourceToken;

            sourceRecord = ActivityRecord.isInAnyTask(resultTo);
            if (sourceRecord == null) {
                throw new SecurityException("Called with bad activity token: " + resultTo);
            }
            if (sourceRecord.app == null) {
                throw new SecurityException("Called without a process attached to activity");
            }

            final ComponentName componentName;
            if (permissionToken != null) {
                // To even attempt to use a permissionToken, an app must also have this signature
                // permission.
                mAmInternal.enforceCallingPermission(
                        android.Manifest.permission.START_ACTIVITY_AS_CALLER,
                        "startActivityAsCaller");
                // If called with a permissionToken, we want the sourceRecord from the delegator
                // activity that requested this token.
                sourceToken = mStartActivitySources.remove(permissionToken);
                if (sourceToken == null) {
                    // Invalid permissionToken, check if it recently expired.
                // If called with a permissionToken, the caller must be the same component that
                // was allowed to use the permissionToken.
                componentName = mStartActivitySources.remove(permissionToken);
                if (!sourceRecord.mActivityComponent.equals(componentName)) {
                    if (mExpiredStartAsCallerTokens.contains(permissionToken)) {
                        throw new SecurityException("Called with expired permission token: "
                                + permissionToken);
@@ -1577,25 +1585,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
                    }
                }
            } else {
                // This method was called directly by the source.
                sourceToken = resultTo;
            }

            sourceRecord = ActivityRecord.isInAnyTask(sourceToken);
            if (sourceRecord == null) {
                throw new SecurityException("Called with bad activity token: " + sourceToken);
            }
            if (sourceRecord.app == null) {
                throw new SecurityException("Called without a process attached to activity");
            }

                // Whether called directly or from a delegate, the source activity must be from the
                // android package.
                if (!sourceRecord.info.packageName.equals("android")) {
                    throw new SecurityException("Must be called from an activity that is "
                            + "declared in the android package");
                }

                if (UserHandle.getAppId(sourceRecord.app.mUid) != SYSTEM_UID) {
                    // This is still okay, as long as this activity is running under the
                    // uid of the original calling activity.
@@ -1606,6 +1601,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
                                        + sourceRecord.launchedFromUid);
                    }
                }
            }
            if (ignoreTargetSecurity) {
                if (intent.getComponent() == null) {
                    throw new SecurityException(