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

Commit 3562f1f8 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Prepare comments and annotations for public FabricatedOverlay API"

parents 4f869ffb ccc347f1
Loading
Loading
Loading
Loading
+310 −96
Original line number Diff line number Diff line
@@ -35,29 +35,84 @@ import java.util.ArrayList;
import java.util.Objects;

/**
 * Fabricated Runtime Resource Overlays (FRROs) are overlays generated ar runtime.
 * FabricatedOverlay describes the content of Fabricated Runtime Resource Overlay (FRRO) that is
 * used to overlay the app's resources. The app should register the {@link FabricatedOverlay}
 * instance in an {@link OverlayManagerTransaction} by calling {@link
 * OverlayManagerTransaction#registerFabricatedOverlay(FabricatedOverlay)}. The FRRO is
 * created once the transaction is committed successfully.
 *
 * Fabricated overlays are enabled, disabled, and reordered just like normal overlays. The
 * overlayable policies a fabricated overlay fulfills are the same policies the creator of the
 * overlay fulfill. For example, a fabricated overlay created by a platform signed package on the
 * system partition would fulfil the {@code system} and {@code signature} policies.
 * <p>The app creates a FabricatedOverlay to describe the how to overlay string, integer, and file
 * type resources. Before creating any frro, please define a target overlayable in {@code
 * res/values/overlayable.xml} that describes what kind of resources can be overlaid, what kind of
 * roles or applications can overlay the resources. Here is an example.
 *
 * The owner of a fabricated overlay is the UID that created it. Overlays commit to the overlay
 * manager persist across reboots. When the UID is uninstalled, its fabricated overlays are wiped.
 * <pre>{@code
 * <overlayable name="SignatureOverlayable" actor="overlay://theme">
 *     <!-- The app with the same signature can overlay the below resources -->
 *     <policy type="signature">
 *         <item type="color" name="mycolor" />
 *         <item type="string" name="mystring" />
 *     </policy>
 * </overlayable>
 * }</pre>
 *
 * Processes with {@link Android.Manifest.permission.CHANGE_OVERLAY_PACKAGES} can manage normal
 * overlays and fabricated overlays.
 * <p>The overlay must assign the target overlayable name just like the above example by calling
 * {@link #setTargetOverlayable(String)}. Here is an example:
 *
 * <pre>{@code
 * FabricatedOverlay fabricatedOverlay = new FabricatedOverlay("overlay_name",
 *                                                             context.getPackageName());
 * fabricatedOverlay.setTargetOverlayable("SignatureOverlayable")
 * fabricatedOverlay.setResourceValue("mycolor", TypedValue.TYPE_INT_COLOR_ARGB8, Color.White)
 * fabricatedOverlay.setResourceValue("mystring", TypedValue.TYPE_STRING, "Hello")
 * }</pre>
 *
 * <p>The app can create any {@link FabricatedOverlay} instance by calling the following APIs.
 *
 * <ul>
 *   <li>{@link #setTargetOverlayable(String)}
 *   <li>{@link #setResourceValue(String, int, int, String)}
 *   <li>{@link #setResourceValue(String, int, String, String)}
 *   <li>{@link #setResourceValue(String, ParcelFileDescriptor, String)}
 * </ul>
 *
 * @see OverlayManager
 * @see OverlayManagerTransaction
 * @hide
 */
public class FabricatedOverlay {

    /** Retrieves the identifier for this fabricated overlay. */
    /**
     * Retrieves the identifier for this fabricated overlay.
     * @return the overlay identifier
     *
     * @hide
     */
    public OverlayIdentifier getIdentifier() {
        return new OverlayIdentifier(
                mOverlay.packageName, TextUtils.nullIfEmpty(mOverlay.overlayName));
    }

    public static class Builder {
    /**
     * The builder of Fabricated Runtime Resource Overlays(FRROs).
     *
     * Fabricated overlays are enabled, disabled, and reordered just like normal overlays. The
     * overlayable policies a fabricated overlay fulfills are the same policies the creator of the
     * overlay fulfill. For example, a fabricated overlay created by a platform signed package on
     * the system partition would fulfil the {@code system} and {@code signature} policies.
     *
     * The owner of a fabricated overlay is the UID that created it. Overlays commit to the overlay
     * manager persist across reboots. When the UID is uninstalled, its fabricated overlays are
     * wiped.
     *
     * Processes with {@code android.Manifest.permission#CHANGE_OVERLAY_PACKAGES} can manage normal
     * overlays and fabricated overlays.
     *
     * @see FabricatedOverlay
     * @see OverlayManagerTransaction.Builder#registerFabricatedOverlay(FabricatedOverlay)
     * @hide
     */
    public static final class Builder {
        private final String mOwningPackage;
        private final String mName;
        private final String mTargetPackage;
@@ -88,22 +143,16 @@ public class FabricatedOverlay {
        }

        /**
         * Constructs a builder for building a fabricated overlay.
         * Sets the name of the target overlayable to be overlaid.
         *
         * @param name a name used to uniquely identify the fabricated overlay owned by the caller
         *             itself.
         * @param targetPackage the name of the package to overlay
         */
        public Builder(@NonNull String name, @NonNull String targetPackage) {
            mName = OverlayManagerImpl.checkOverlayNameValid(name);
            mTargetPackage =
                    Preconditions.checkStringNotEmpty(
                            targetPackage, "'targetPackage' must not be empty nor null");
            mOwningPackage = ""; // The package name is filled in OverlayManager.commit
        }

        /**
         * Sets the name of the overlayable resources to overlay (can be null).
         * <p>The target package defines may define several overlayables. The
         * {@link FabricatedOverlay} should specify which overlayable to be overlaid.
         *
         * <p>The target overlayable should be defined in {@code <overlayable>} and pass the value
         * of its {@code name} attribute as the parameter.
         *
         * @param targetOverlayable is a name of the overlayable resources set
         * @hide
         */
        @NonNull
        public Builder setTargetOverlayable(@Nullable String targetOverlayable) {
@@ -111,27 +160,6 @@ public class FabricatedOverlay {
            return this;
        }

        /**
         * Ensure the resource name is in the form [package]:type/entry.
         *
         * @param name name of the target resource to overlay (in the form [package]:type/entry)
         * @return the valid name
         */
        private static String ensureValidResourceName(@NonNull String name) {
            Objects.requireNonNull(name);
            final int slashIndex = name.indexOf('/'); /* must contain '/' */
            final int colonIndex = name.indexOf(':'); /* ':' should before '/' if ':' exist */

            // The minimum length of resource type is "id".
            Preconditions.checkArgument(
                    slashIndex >= 0 /* It must contain the type name */
                    && colonIndex != 0 /* 0 means the package name is empty */
                    && (slashIndex - colonIndex) > 2 /* The shortest length of type is "id" */,
                    "\"%s\" is invalid resource name",
                    name);
            return name;
        }

        /**
         * Sets the value of the fabricated overlay for the integer-like types.
         *
@@ -141,8 +169,12 @@ public class FabricatedOverlay {
         * @param value the unsigned 32 bit integer representing the new value
         * @return the builder itself
         * @see #setResourceValue(String, int, int, String)
         * @see android.util.TypedValue#type
         * @see android.util.TypedValue#TYPE_INT_COLOR_ARGB8 android.util.TypedValue#type
         * @deprecated Framework should use {@link FabricatedOverlay#setResourceValue(String, int,
                       int, String)} instead.
         * @hide
         */
        @Deprecated(since = "Please use FabricatedOverlay#setResourceValue instead")
        @NonNull
        public Builder setResourceValue(
                @NonNull String resourceName,
@@ -161,8 +193,13 @@ public class FabricatedOverlay {
         * @param dataType the data type of the new value
         * @param value the unsigned 32 bit integer representing the new value
         * @param configuration The string representation of the config this overlay is enabled for
         * @see android.util.TypedValue#type
         * @return the builder itself
         * @see android.util.TypedValue#TYPE_INT_COLOR_ARGB8 android.util.TypedValue#type
         * @deprecated Framework should use {@link FabricatedOverlay#setResourceValue(String, int,
                       int, String)} instead.
         * @hide
         */
        @Deprecated(since = "Please use FabricatedOverlay#setResourceValue instead")
        @NonNull
        public Builder setResourceValue(
                @NonNull String resourceName,
@@ -171,30 +208,11 @@ public class FabricatedOverlay {
                int value,
                @Nullable String configuration) {
            ensureValidResourceName(resourceName);

            final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry();
            entry.resourceName = resourceName;
            entry.dataType =
                    Preconditions.checkArgumentInRange(
                            dataType,
                            TypedValue.TYPE_FIRST_INT,
                            TypedValue.TYPE_LAST_INT,
                            "dataType");
            entry.data = value;
            entry.configuration = configuration;
            mEntries.add(entry);
            mEntries.add(generateFabricatedOverlayInternalEntry(resourceName, dataType, value,
                    configuration));
            return this;
        }

        /** @hide */
        @IntDef(
                prefix = {"OVERLAY_TYPE"},
                value = {
                    TypedValue.TYPE_STRING,
                })
        @Retention(RetentionPolicy.SOURCE)
        public @interface StringTypeOverlayResource {}

        /**
         * Sets the value of the fabricated overlay for the string-like type.
         *
@@ -203,8 +221,12 @@ public class FabricatedOverlay {
         * @param dataType the data type of the new value
         * @param value the string representing the new value
         * @return the builder itself
         * @see android.util.TypedValue#type
         * @see android.util.TypedValue#TYPE_STRING android.util.TypedValue#type
         * @deprecated Framework should use {@link FabricatedOverlay#setResourceValue(String, int,
                       String, String)} instead.
         * @hide
         */
        @Deprecated(since = "Please use FabricatedOverlay#setResourceValue instead")
        @NonNull
        public Builder setResourceValue(
                @NonNull String resourceName,
@@ -221,8 +243,13 @@ public class FabricatedOverlay {
         * @param dataType the data type of the new value
         * @param value the string representing the new value
         * @param configuration The string representation of the config this overlay is enabled for
         * @see android.util.TypedValue#type
         * @return the builder itself
         * @see android.util.TypedValue#TYPE_STRING android.util.TypedValue#type
         * @deprecated Framework should use {@link FabricatedOverlay#setResourceValue(String, int,
                       String, String)} instead.
         * @hide
         */
        @Deprecated(since = "Please use FabricatedOverlay#setResourceValue instead")
        @NonNull
        public Builder setResourceValue(
                @NonNull String resourceName,
@@ -230,39 +257,32 @@ public class FabricatedOverlay {
                @NonNull String value,
                @Nullable String configuration) {
            ensureValidResourceName(resourceName);

            final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry();
            entry.resourceName = resourceName;
            entry.dataType =
                    Preconditions.checkArgumentInRange(
                            dataType, TypedValue.TYPE_STRING, TypedValue.TYPE_FRACTION, "dataType");
            entry.stringData = Objects.requireNonNull(value);
            entry.configuration = configuration;
            mEntries.add(entry);
            mEntries.add(generateFabricatedOverlayInternalEntry(resourceName, dataType, value,
                    configuration));
            return this;
        }

        /**
         * Sets the value of the fabricated overlay
         * Sets the value of the fabricated overlay for the file descriptor type.
         *
         * @param resourceName name of the target resource to overlay (in the form
         *     [package]:type/entry)
         * @param value the file descriptor whose contents are the value of the frro
         * @param configuration The string representation of the config this overlay is enabled for
         * @return the builder itself
         * @deprecated Framework should use {@link FabricatedOverlay#setResourceValue(String,
                       ParcelFileDescriptor, String)} instead.
         * @hide
         */
        @Deprecated(since = "Please use FabricatedOverlay#setResourceValue instead")
        @NonNull
        public Builder setResourceValue(
                @NonNull String resourceName,
                @NonNull ParcelFileDescriptor value,
                @Nullable String configuration) {
            ensureValidResourceName(resourceName);

            final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry();
            entry.resourceName = resourceName;
            entry.binaryData = Objects.requireNonNull(value);
            entry.configuration = configuration;
            mEntries.add(entry);
            mEntries.add(
                    generateFabricatedOverlayInternalEntry(resourceName, value, configuration));
            return this;
        }

@@ -270,22 +290,216 @@ public class FabricatedOverlay {
         * Builds an immutable fabricated overlay.
         *
         * @return the fabricated overlay
         * @hide
         */
        @NonNull
        public FabricatedOverlay build() {
            return new FabricatedOverlay(
                    generateFabricatedOverlayInternal(mOwningPackage, mName, mTargetPackage,
                            mTargetOverlayable, mEntries));
        }
    }

    private static FabricatedOverlayInternal generateFabricatedOverlayInternal(
            @NonNull String owningPackage, @NonNull String overlayName,
            @NonNull String targetPackageName, @Nullable String targetOverlayable,
            @NonNull ArrayList<FabricatedOverlayInternalEntry> entries) {
        final FabricatedOverlayInternal overlay = new FabricatedOverlayInternal();
            overlay.packageName = mOwningPackage;
            overlay.overlayName = mName;
            overlay.targetPackageName = mTargetPackage;
            overlay.targetOverlayable = mTargetOverlayable;
        overlay.packageName = owningPackage;
        overlay.overlayName = overlayName;
        overlay.targetPackageName = targetPackageName;
        overlay.targetOverlayable = TextUtils.emptyIfNull(targetOverlayable);
        overlay.entries = new ArrayList<>();
            overlay.entries.addAll(mEntries);
            return new FabricatedOverlay(overlay);
        }
        overlay.entries.addAll(entries);
        return overlay;
    }

    final FabricatedOverlayInternal mOverlay;
    private FabricatedOverlay(FabricatedOverlayInternal overlay) {
        mOverlay = overlay;
    }

    /**
     * Create a fabricated overlay to overlay on the specified package.
     *
     * @param overlayName a name used to uniquely identify the fabricated overlay owned by the
     *                   caller itself.
     * @param targetPackage the name of the package to be overlaid
     * @hide
     */
    public FabricatedOverlay(@NonNull String overlayName, @NonNull String targetPackage) {
        this(generateFabricatedOverlayInternal(
                "" /* owningPackage, The package name is filled commitment */,
                OverlayManagerImpl.checkOverlayNameValid(overlayName),
                Preconditions.checkStringNotEmpty(targetPackage,
                        "'targetPackage' must not be empty nor null"),
                null /* targetOverlayable */,
                new ArrayList<>()));
    }

    /**
     * Set the target overlayable name of the overlay
     *
     * The target package defines may define several overlayables. The {@link FabricatedOverlay}
     * should specify which overlayable to be overlaid.
     *
     * @param targetOverlayable the overlayable name defined in target package.
     * @hide
     */
    public void setTargetOverlayable(@Nullable String targetOverlayable) {
        mOverlay.targetOverlayable = TextUtils.emptyIfNull(targetOverlayable);
    }

    /**
     * Return the target overlayable name of the overlay
     *
     * The target package defines may define several overlayables. The {@link FabricatedOverlay}
     * should specify which overlayable to be overlaid.
     *
     * @return the target overlayable name.
     * @hide
     */
    @Nullable
    public String getTargetOverlayable() {
        return mOverlay.targetOverlayable;
    }

    /**
     * Ensure the resource name is in the form [package]:type/entry.
     *
     * @param name name of the target resource to overlay (in the form [package]:type/entry)
     * @return the valid name
     */
    private static String ensureValidResourceName(@NonNull String name) {
        Objects.requireNonNull(name);
        final int slashIndex = name.indexOf('/'); /* must contain '/' */
        final int colonIndex = name.indexOf(':'); /* ':' should before '/' if ':' exist */

        // The minimum length of resource type is "id".
        Preconditions.checkArgument(
                slashIndex >= 0 /* It must contain the type name */
                        && colonIndex != 0 /* 0 means the package name is empty */
                        && (slashIndex - colonIndex) > 2 /* The shortest length of type is "id" */,
                "\"%s\" is invalid resource name",
                name);
        return name;
    }

    @NonNull
    private static FabricatedOverlayInternalEntry generateFabricatedOverlayInternalEntry(
            @NonNull String resourceName,
            @IntRange(from = TypedValue.TYPE_FIRST_INT, to = TypedValue.TYPE_LAST_INT) int dataType,
            int value, @Nullable String configuration) {
        final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry();
        entry.resourceName = resourceName;
        entry.dataType =
                Preconditions.checkArgumentInRange(
                        dataType,
                        TypedValue.TYPE_FIRST_INT,
                        TypedValue.TYPE_LAST_INT,
                        "dataType");
        entry.data = value;
        entry.configuration = configuration;
        return entry;
    }

    @NonNull
    private static FabricatedOverlayInternalEntry generateFabricatedOverlayInternalEntry(
            @NonNull String resourceName, @StringTypeOverlayResource int dataType,
            @NonNull String value, @Nullable String configuration) {
        final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry();
        entry.resourceName = resourceName;
        entry.dataType =
                Preconditions.checkArgumentInRange(
                        dataType, TypedValue.TYPE_STRING, TypedValue.TYPE_FRACTION, "dataType");
        entry.stringData = Objects.requireNonNull(value);
        entry.configuration = configuration;
        return entry;
    }

    @NonNull
    private static FabricatedOverlayInternalEntry generateFabricatedOverlayInternalEntry(
            @NonNull String resourceName, @NonNull ParcelFileDescriptor parcelFileDescriptor,
            @Nullable String configuration) {
        final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry();
        entry.resourceName = resourceName;
        entry.binaryData = Objects.requireNonNull(parcelFileDescriptor);
        entry.configuration = configuration;
        return entry;
    }

    /**
     * Sets the resource value in the fabricated overlay for the integer-like types with the
     * configuration.
     *
     * @param resourceName name of the target resource to overlay (in the form
     *     [package]:type/entry)
     * @param dataType the data type of the new value
     * @param value the integer representing the new value
     * @param configuration The string representation of the config this overlay is enabled for
     * @see android.util.TypedValue#TYPE_INT_COLOR_ARGB8 android.util.TypedValue#type
     * @hide
     */
    @NonNull
    public void setResourceValue(
            @NonNull String resourceName,
            @IntRange(from = TypedValue.TYPE_FIRST_INT, to = TypedValue.TYPE_LAST_INT) int dataType,
            int value,
            @Nullable String configuration) {
        ensureValidResourceName(resourceName);
        mOverlay.entries.add(generateFabricatedOverlayInternalEntry(resourceName, dataType, value,
                configuration));
    }

    /** @hide */
    @IntDef(
            prefix = {"OVERLAY_TYPE"},
            value = {
                    TypedValue.TYPE_STRING,
            })
    @Retention(RetentionPolicy.SOURCE)
    public @interface StringTypeOverlayResource {}

    /**
     * Sets the resource value in the fabricated overlay for the string-like type with the
     * configuration.
     *
     * @param resourceName name of the target resource to overlay (in the form
     *     [package]:type/entry)
     * @param dataType the data type of the new value
     * @param value the string representing the new value
     * @param configuration The string representation of the config this overlay is enabled for
     * @see android.util.TypedValue#TYPE_STRING android.util.TypedValue#type
     * @hide
     */
    @NonNull
    public void setResourceValue(
            @NonNull String resourceName,
            @StringTypeOverlayResource int dataType,
            @NonNull String value,
            @Nullable String configuration) {
        ensureValidResourceName(resourceName);
        mOverlay.entries.add(generateFabricatedOverlayInternalEntry(resourceName, dataType, value,
                configuration));
    }

    /**
     * Sets the resource value in the fabricated overlay for the file descriptor type with the
     * configuration.
     *
     * @param resourceName name of the target resource to overlay (in the form
     *     [package]:type/entry)
     * @param value the file descriptor whose contents are the value of the frro
     * @param configuration The string representation of the config this overlay is enabled for
     * @hide
     */
    @NonNull
    public void setResourceValue(
            @NonNull String resourceName,
            @NonNull ParcelFileDescriptor value,
            @Nullable String configuration) {
        ensureValidResourceName(resourceName);
        mOverlay.entries.add(
                generateFabricatedOverlayInternalEntry(resourceName, value, configuration));
    }
}