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

Commit a4a51550 authored by Azhara Assanova's avatar Azhara Assanova
Browse files

Add Activity manifest attribute: requireContentUriPermissionFromCaller

The new attribute specifies permissions necessary for launching the
activity via startActivity() when passing content URIs. The default
value is none, meaning no specific permissions are required for the
content URIs. Setting this attribute to other values will enforce the
invoker to have the required permissions. If they fail to have them,
the activity start is denied via a SecurityException. The enforcement
works for content URIs in Intent#getData and Intent#getClipData.

Bug: 293467489
Test: atest WmTests:ActivityStarterTests
Test: atest CtsPackageManagerTestCases:android.content.pm.cts.ActivityInfoTest
Test: atest CtsContentTestCasesRavenwood:android.content.pm.cts.ActivityInfoTest
Test: atest FrameworksServicesTests:com.android.server.uri
Test: atest FrameworksServicesTestsRavenwood:com.android.server.uri.UriGrantsManagerServiceTest
Test: atest CtsContentTestCases:android.content.cts.ActivityRequireContentUriPermissionFromCallerTest
Change-Id: Ifca162d31fa789caa5943b78d1db77c51a5d7f80
parent 43dfcd48
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1375,6 +1375,7 @@ package android {
    field public static final int reqTouchScreen = 16843303; // 0x1010227
    field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603
    field public static final int requestRawExternalStorageAccess = 16844357; // 0x1010645
    field @FlaggedApi("android.security.content_uri_permission_apis") public static final int requireContentUriPermissionFromCaller;
    field public static final int requireDeviceScreenOn = 16844317; // 0x101061d
    field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
    field public static final int required = 16843406; // 0x101028e
+112 −0
Original line number Diff line number Diff line
@@ -238,6 +238,110 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
    @Nullable
    public String requiredDisplayCategory;

    /**
     * Constant corresponding to {@code none} in the
     * {@link android.R.attr#requireContentUriPermissionFromCaller} attribute.
     * @hide
     */
    public static final int CONTENT_URI_PERMISSION_NONE = 0;

    /**
     * Constant corresponding to {@code read} in the
     * {@link android.R.attr#requireContentUriPermissionFromCaller} attribute.
     * @hide
     */
    public static final int CONTENT_URI_PERMISSION_READ = 1;

    /**
     * Constant corresponding to {@code write} in the
     * {@link android.R.attr#requireContentUriPermissionFromCaller} attribute.
     * @hide
     */
    public static final int CONTENT_URI_PERMISSION_WRITE = 2;

    /**
     * Constant corresponding to {@code readOrWrite} in the
     * {@link android.R.attr#requireContentUriPermissionFromCaller} attribute.
     * @hide
     */
    public static final int CONTENT_URI_PERMISSION_READ_OR_WRITE = 3;

    /**
     * Constant corresponding to {@code readAndWrite} in the
     * {@link android.R.attr#requireContentUriPermissionFromCaller} attribute.
     * @hide
     */
    public static final int CONTENT_URI_PERMISSION_READ_AND_WRITE = 4;

    /** @hide */
    @SuppressWarnings("SwitchIntDef")
    public static boolean isRequiredContentUriPermissionRead(
            @RequiredContentUriPermission int permission) {
        return switch (permission) {
            case CONTENT_URI_PERMISSION_READ_AND_WRITE, CONTENT_URI_PERMISSION_READ_OR_WRITE,
                    CONTENT_URI_PERMISSION_READ -> true;
            default -> false;
        };
    }

    /** @hide */
    @SuppressWarnings("SwitchIntDef")
    public static boolean isRequiredContentUriPermissionWrite(
            @RequiredContentUriPermission int permission) {
        return switch (permission) {
            case CONTENT_URI_PERMISSION_READ_AND_WRITE, CONTENT_URI_PERMISSION_READ_OR_WRITE,
                    CONTENT_URI_PERMISSION_WRITE -> true;
            default -> false;
        };
    }

    /** @hide */
    @IntDef(prefix = "CONTENT_URI_PERMISSION_", value = {
            CONTENT_URI_PERMISSION_NONE,
            CONTENT_URI_PERMISSION_READ,
            CONTENT_URI_PERMISSION_WRITE,
            CONTENT_URI_PERMISSION_READ_OR_WRITE,
            CONTENT_URI_PERMISSION_READ_AND_WRITE
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface RequiredContentUriPermission {
    }

    private String requiredContentUriPermissionToFullString(
            @RequiredContentUriPermission int permission) {
        return switch (permission) {
            case CONTENT_URI_PERMISSION_NONE -> "CONTENT_URI_PERMISSION_NONE";
            case CONTENT_URI_PERMISSION_READ -> "CONTENT_URI_PERMISSION_READ";
            case CONTENT_URI_PERMISSION_WRITE -> "CONTENT_URI_PERMISSION_WRITE";
            case CONTENT_URI_PERMISSION_READ_OR_WRITE -> "CONTENT_URI_PERMISSION_READ_OR_WRITE";
            case CONTENT_URI_PERMISSION_READ_AND_WRITE -> "CONTENT_URI_PERMISSION_READ_AND_WRITE";
            default -> "unknown=" + permission;
        };
    }

    /** @hide */
    public static String requiredContentUriPermissionToShortString(
            @RequiredContentUriPermission int permission) {
        return switch (permission) {
            case CONTENT_URI_PERMISSION_NONE -> "none";
            case CONTENT_URI_PERMISSION_READ -> "read";
            case CONTENT_URI_PERMISSION_WRITE -> "write";
            case CONTENT_URI_PERMISSION_READ_OR_WRITE -> "read or write";
            case CONTENT_URI_PERMISSION_READ_AND_WRITE -> "read and write";
            default -> "unknown=" + permission;
        };
    }

    /**
     * Specifies permissions necessary to launch this activity via
     * {@link android.content.Context#startActivity} when passing content URIs. The default value is
     * {@code none}, meaning no specific permissions are required. Setting this attribute restricts
     * activity invocation based on the invoker's permissions.
     * @hide
     */
    @RequiredContentUriPermission
    public int requireContentUriPermissionFromCaller;

    /**
     * Activity can not be resized and always occupies the fullscreen area with all windows fully
     * visible.
@@ -1590,6 +1694,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
        mMinAspectRatio = orig.mMinAspectRatio;
        supportsSizeChanges = orig.supportsSizeChanges;
        requiredDisplayCategory = orig.requiredDisplayCategory;
        requireContentUriPermissionFromCaller = orig.requireContentUriPermissionFromCaller;
    }

    /**
@@ -1946,6 +2051,11 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
        if (requiredDisplayCategory != null) {
            pw.println(prefix + "requiredDisplayCategory=" + requiredDisplayCategory);
        }
        if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
            pw.println(prefix + "requireContentUriPermissionFromCaller="
                    + requiredContentUriPermissionToFullString(
                            requireContentUriPermissionFromCaller));
        }
        super.dumpBack(pw, prefix, dumpFlags);
    }

@@ -1993,6 +2103,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
        dest.writeBoolean(supportsSizeChanges);
        sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags);
        dest.writeString8(requiredDisplayCategory);
        dest.writeInt(requireContentUriPermissionFromCaller);
    }

    /**
@@ -2119,6 +2230,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
            mKnownActivityEmbeddingCerts = null;
        }
        requiredDisplayCategory = source.readString8();
        requireContentUriPermissionFromCaller = source.readInt();
    }

    /**
+3 −0
Original line number Diff line number Diff line
@@ -89,4 +89,7 @@ public interface ParsedActivity extends ParsedMainComponent {
     */
    @Nullable
    String getRequiredDisplayCategory();

    /** Gets the permissions necessary for launching the activity when using content URIs. */
    int getRequireContentUriPermissionFromCaller();
}
+24 −4
Original line number Diff line number Diff line
@@ -36,9 +36,9 @@ import android.os.Parcelable;
import android.text.TextUtils;
import android.util.ArraySet;

import com.android.internal.pm.pkg.parsing.ParsingUtils;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
import com.android.internal.pm.pkg.parsing.ParsingUtils;

import java.util.Collections;
import java.util.Locale;
@@ -97,6 +97,8 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
    @Nullable
    private String mRequiredDisplayCategory;

    private int mRequireContentUriPermissionFromCaller;

    public ParsedActivityImpl(ParsedActivityImpl other) {
        super(other);
        this.theme = other.theme;
@@ -124,6 +126,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
        this.windowLayout = other.windowLayout;
        this.mKnownActivityEmbeddingCerts = other.mKnownActivityEmbeddingCerts;
        this.mRequiredDisplayCategory = other.mRequiredDisplayCategory;
        this.mRequireContentUriPermissionFromCaller = other.mRequireContentUriPermissionFromCaller;
    }

    /**
@@ -192,6 +195,8 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
        alias.setDirectBootAware(target.isDirectBootAware());
        alias.setProcessName(target.getProcessName());
        alias.setRequiredDisplayCategory(target.getRequiredDisplayCategory());
        alias.setRequireContentUriPermissionFromCaller(
                target.getRequireContentUriPermissionFromCaller());
        return alias;

        // Not all attributes from the target ParsedActivity are copied to the alias.
@@ -320,6 +325,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
        }
        sForStringSet.parcel(this.mKnownActivityEmbeddingCerts, dest, flags);
        dest.writeString8(this.mRequiredDisplayCategory);
        dest.writeInt(this.mRequireContentUriPermissionFromCaller);
    }

    public ParsedActivityImpl() {
@@ -355,6 +361,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
        }
        this.mKnownActivityEmbeddingCerts = sForStringSet.unparcel(in);
        this.mRequiredDisplayCategory = in.readString8();
        this.mRequireContentUriPermissionFromCaller = in.readInt();
    }

    @NonNull
@@ -412,7 +419,8 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
            int rotationAnimation,
            int colorMode,
            @Nullable ActivityInfo.WindowLayout windowLayout,
            @Nullable String requiredDisplayCategory) {
            @Nullable String requiredDisplayCategory,
            int requireContentUriPermissionFromCaller) {
        this.theme = theme;
        this.uiOptions = uiOptions;
        this.targetActivity = targetActivity;
@@ -438,6 +446,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
        this.colorMode = colorMode;
        this.windowLayout = windowLayout;
        this.mRequiredDisplayCategory = requiredDisplayCategory;
        this.mRequireContentUriPermissionFromCaller = requireContentUriPermissionFromCaller;

        // onConstructed(); // You can define this method to get a callback
    }
@@ -562,6 +571,11 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
        return mRequiredDisplayCategory;
    }

    @DataClass.Generated.Member
    public int getRequireContentUriPermissionFromCaller() {
        return mRequireContentUriPermissionFromCaller;
    }

    @DataClass.Generated.Member
    public @NonNull ParsedActivityImpl setTheme( int value) {
        theme = value;
@@ -694,11 +708,17 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
        return this;
    }

    @DataClass.Generated.Member
    public @NonNull ParsedActivityImpl setRequireContentUriPermissionFromCaller( int value) {
        mRequireContentUriPermissionFromCaller = value;
        return this;
    }

    @DataClass.Generated(
            time = 1701338377709L,
            time = 1706180262165L,
            codegenVersion = "1.0.23",
            sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java",
            inputSignatures = "private  int theme\nprivate  int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate  int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate  int launchMode\nprivate  int documentLaunchMode\nprivate  int maxRecents\nprivate  int configChanges\nprivate  int softInputMode\nprivate  int persistableMode\nprivate  int lockTaskLaunchMode\nprivate  int screenOrientation\nprivate  int resizeMode\nprivate  float maxAspectRatio\nprivate  float minAspectRatio\nprivate  boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate  int rotationAnimation\nprivate  int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mRequiredDisplayCategory\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedActivityImpl> CREATOR\npublic static @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.internal.pm.pkg.component.ParsedActivity)\npublic  com.android.internal.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic  com.android.internal.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic  com.android.internal.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic  com.android.internal.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic  void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.internal.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
            inputSignatures = "private  int theme\nprivate  int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate  int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate  int launchMode\nprivate  int documentLaunchMode\nprivate  int maxRecents\nprivate  int configChanges\nprivate  int softInputMode\nprivate  int persistableMode\nprivate  int lockTaskLaunchMode\nprivate  int screenOrientation\nprivate  int resizeMode\nprivate  float maxAspectRatio\nprivate  float minAspectRatio\nprivate  boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate  int rotationAnimation\nprivate  int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mRequiredDisplayCategory\nprivate  int mRequireContentUriPermissionFromCaller\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedActivityImpl> CREATOR\npublic static @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.internal.pm.pkg.component.ParsedActivity)\npublic  com.android.internal.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic  com.android.internal.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic  com.android.internal.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic  com.android.internal.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic  void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.internal.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
    @Deprecated
    private void __metadata() {}

+4 −0
Original line number Diff line number Diff line
@@ -241,6 +241,10 @@ public class ParsedActivityUtils {

            activity.setRequiredDisplayCategory(requiredDisplayCategory);

            activity.setRequireContentUriPermissionFromCaller(sa.getInt(
                    R.styleable.AndroidManifestActivity_requireContentUriPermissionFromCaller,
                    ActivityInfo.CONTENT_URI_PERMISSION_NONE));

            return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, receiver,
                    false /*isAlias*/, visibleToEphemeral, input,
                    R.styleable.AndroidManifestActivity_parentActivityName,
Loading