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

Commit ccc347f1 authored by felkachang's avatar felkachang
Browse files

Prepare comments and annotations for public FabricatedOverlay API

This patch adds the following items.
* FabricatedOverlay class comments.
* add a FabricatedOverlay contructor
* add a public method `setTargetOverlayable`
* add setResourceValue APIs for
  * int
  * String
  * ParcelFileDescriptor
* comments and annotations for methods in FabricatedOverlay.
  * such as nullability

Bug: 205919743
Test: atest \
        OverlayHostTests \
        OverlayDeviceTests \
        SelfTargetingOverlayDeviceTests \
        OverlayRemountedTest \
        FrameworksServicesTests:com.android.server.om \
        CtsContentTestCases:android.content.om.cts \
        idmap2_tests
Change-Id: I689f5882fc3e1fc74a343106ef310f23a83990cf
parent d9c0b685
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));
    }
}