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

Commit e9b99f82 authored by Yohei Yukawa's avatar Yohei Yukawa Committed by Android (Google) Code Review
Browse files

Merge changes I7918c0a3,I19d87fc1

* changes:
  Support content URIs w/ userId in IC#commitContent
  Fix up content URI for different users
parents a4438c49 3933a6e0
Loading
Loading
Loading
Loading
+31 −1
Original line number Diff line number Diff line
@@ -18,11 +18,14 @@ package android.view.inputmethod;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ClipDescription;
import android.content.ContentProvider;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.UserHandle;

import com.android.internal.inputmethod.IInputContentUriToken;

@@ -33,8 +36,24 @@ import java.security.InvalidParameterException;
 */
public final class InputContentInfo implements Parcelable {

    /**
     * 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.  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>
     */
    @UserIdInt
    private final int mContentUriOwnerUserId;
    @NonNull
    private final ClipDescription mDescription;
    @Nullable
@@ -73,6 +92,8 @@ public final class InputContentInfo implements Parcelable {
            @Nullable Uri linkUri) {
        validateInternal(contentUri, description, linkUri, true /* throwException */);
        mContentUri = contentUri;
        mContentUriOwnerUserId =
                ContentProvider.getUserIdFromUri(mContentUri, UserHandle.myUserId());
        mDescription = description;
        mLinkUri = linkUri;
    }
@@ -139,7 +160,14 @@ public final class InputContentInfo implements Parcelable {
     * @return Content URI with which the content can be obtained.
     */
    @NonNull
    public Uri getContentUri() { return mContentUri; }
    public Uri getContentUri() {
        // Fix up the content URI when and only when the caller's user ID does not match the owner's
        // user ID.
        if (mContentUriOwnerUserId != UserHandle.myUserId()) {
            return ContentProvider.maybeAddUserId(mContentUri, mContentUriOwnerUserId);
        }
        return mContentUri;
    }

    /**
     * @return {@link ClipDescription} object that contains the metadata of {@code #getContentUri()}
@@ -203,6 +231,7 @@ public final class InputContentInfo implements Parcelable {
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        Uri.writeToParcel(dest, mContentUri);
        dest.writeInt(mContentUriOwnerUserId);
        mDescription.writeToParcel(dest, flags);
        Uri.writeToParcel(dest, mLinkUri);
        if (mUriToken != null) {
@@ -215,6 +244,7 @@ public final class InputContentInfo implements Parcelable {

    private InputContentInfo(@NonNull Parcel source) {
        mContentUri = Uri.CREATOR.createFromParcel(source);
        mContentUriOwnerUserId = source.readInt();
        mDescription = ClipDescription.CREATOR.createFromParcel(source);
        mLinkUri = Uri.CREATOR.createFromParcel(source);
        if (source.readInt() == 1) {
+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);
        }
    }