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

Commit 3933a6e0 authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

Support content URIs w/ userId in IC#commitContent

With this CL, one can specify a content URI with an embedded user ID to
InputContentInfo, like such a URI is supported in
Context#grantUriPermission().

Note that such a scenario is actually possible when 1) an application
running as User X sets a content URI to the system clipboard then 2) the
IME runing as User Y who share the clipboard with User X obtains the
content URI from the system and tries to create a new instance of
InputContentInfo.

Bug: 32427307
Bug: 32778718
Test: 'adb shell dumpsys activity permissions' with a custom IME that
      instantiates InputContentInfo from the content URI obtained from
      the clipboard.
Change-Id: I7918c0a379b8f3e7e64b106447b42447876f9057
parent 16e67edb
Loading
Loading
Loading
Loading
+9 −12
Original line number Diff line number Diff line
@@ -37,14 +37,17 @@ import java.security.InvalidParameterException;
public final class InputContentInfo implements Parcelable {

    /**
     * The content URI that never has a user ID embedded by
     * {@link ContentProvider#maybeAddUserId(Uri, int)}.
     * The content URI that may or may not have a user ID embedded by
     * {@link ContentProvider#maybeAddUserId(Uri, int)}.  This always preserves the exact value
     * specified to a constructor.  In other words, if it had user ID embedded when it was passed
     * to the constructor, it still has the same user ID no matter if it is valid or not.
     */
    @NonNull
    private final Uri mContentUri;
    /**
     * The user ID to which {@link #mContentUri} belongs to.  This is always determined based on
     * the process ID when the constructor is called.
     * The user ID to which {@link #mContentUri} belongs to.  If {@link #mContentUri} already
     * embedded the user ID when it was specified then this fields has the same user ID.  Otherwise
     * the user ID is determined based on the process ID when the constructor is called.
     *
     * <p>CAUTION: If you received {@link InputContentInfo} from a different process, there is no
     * guarantee that this value is correct and valid.  Never use this for any security purpose</p>
@@ -89,7 +92,8 @@ public final class InputContentInfo implements Parcelable {
            @Nullable Uri linkUri) {
        validateInternal(contentUri, description, linkUri, true /* throwException */);
        mContentUri = contentUri;
        mContentUriOwnerUserId = UserHandle.myUserId();
        mContentUriOwnerUserId =
                ContentProvider.getUserIdFromUri(mContentUri, UserHandle.myUserId());
        mDescription = description;
        mLinkUri = linkUri;
    }
@@ -138,13 +142,6 @@ public final class InputContentInfo implements Parcelable {
            }
            return false;
        }
        if (ContentProvider.uriHasUserId(contentUri)) {
            if (throwException) {
                throw new InvalidParameterException(
                        "contentUri with a user ID is not currently supported");
            }
            return false;
        }
        if (linkUri != null) {
            final String scheme = linkUri.getScheme();
            if (scheme == null ||
+15 −2
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
@@ -3969,10 +3970,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
                    + mCurAttribute.packageName + " packageName=" + packageName);
                return null;
            }
            // This user ID can never bee spoofed.
            final int imeUserId = UserHandle.getUserId(uid);
            // This user ID can never bee spoofed.
            final int appUserId = UserHandle.getUserId(mCurClient.uid);
            return new InputContentUriTokenHandler(contentUri, uid, packageName, imeUserId,
                    appUserId);
            // This user ID may be invalid if "contentUri" embedded an invalid user ID.
            final int contentUriOwnerUserId = ContentProvider.getUserIdFromUri(contentUri,
                    imeUserId);
            final Uri contentUriWithoutUserId = ContentProvider.getUriWithoutUserId(contentUri);
            // Note: InputContentUriTokenHandler.take() checks whether the IME (specified by "uid")
            // actually has the right to grant a read permission for "contentUriWithoutUserId" that
            // is claimed to belong to "contentUriOwnerUserId".  For example, specifying random
            // content URI and/or contentUriOwnerUserId just results in a SecurityException thrown
            // from InputContentUriTokenHandler.take() and can never be allowed beyond what is
            // actually allowed to "uid", which is guaranteed to be the IME's one.
            return new InputContentUriTokenHandler(contentUriWithoutUserId, uid,
                    packageName, contentUriOwnerUserId, appUserId);
        }
    }