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

Commit ea2f3be7 authored by Todd Lee's avatar Todd Lee
Browse files

OEM single-build/multi-SKU via dynamic RRO support

The purpose here is to provide support for selectively
enabling Runtime Resource Overlays (RROs) (specifically
those pertaining to a specific SKU, within a OEM's "single
build" covering multiple SKUs) at boot based on the value
of a pre-defined system property.

This mechanism is designed to be compatible with other,
recent changes to Runtime Resource Overlays - specifically:

- has no effect on 'isStatic'. Resource overlays must be
  attributed as static in order to qualify for loading into
  the system_server. The 'requiredSystemPropertyName/
  requiredSystemPropertyValue' mechanism operates
  independent of this and can be used on both static and
  non static overlays. The effect of specifying a conditional
  property on any overlay is that it will ONLY be enabled
  in the event that the system reflects both the property
  and the specified value (Note that in the ABSENCE of a
  conditional property, overlays are assumed to be enabled).

- has no effect on OverlayManagerService (OMS) API. The
  OMS provides the system with an interface through which
  overlays can be enabled/disabled and even rearranged at
  runtime. This provides the basis of support for various
  user-level features (e.g. dynamic theme selection).
  The 'requiredSystemPropertyName/requiredSystemPropertyValue'
  mechanism operates independent of this -
  with enablement being completely coupled to the available
  system properties on the device and NOT subject to change
  at runtime.

Note: as part of this change, original overlay tests have been
updated (fixed) and expanded to include tests to cover the
conditional property implementation.

Issue: http://b/35100249
Test: frameworks/base/core/tests/overlaytests/testrunner.py

Change-Id: I1990ce21a27a385db1e2f53294b69dd03988351e
(cherry picked from commit d5566c6c)
parent 5fc6e63e
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -1197,6 +1197,8 @@ package android {
    field public static final int requiredFeature = 16844119; // 0x1010557
    field public static final int requiredForAllUsers = 16843728; // 0x10103d0
    field public static final int requiredNotFeature = 16844120; // 0x1010558
    field public static final int requiredSystemPropertyName = 16844136; // 0x1010568
    field public static final int requiredSystemPropertyValue = 16844137; // 0x1010569
    field public static final int requiresFadingEdge = 16843685; // 0x10103a5
    field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364
    field public static final int resizeClip = 16843983; // 0x10104cf
+37 −27
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
#include <androidfw/StreamingZipInflater.h>
#include <androidfw/ZipFileRO.h>
#include <cutils/jstring.h>
#include <cutils/properties.h>
#include <private/android_filesystem_config.h> // for AID_SYSTEM
#include <utils/SortedVector.h>
#include <utils/String16.h>
@@ -82,12 +83,26 @@ namespace {
        return String8(tmp);
    }

    bool check_property(String16 property, String16 value) {
        const char *prop;
        const char *val;

        prop = strndup16to8(property.string(), property.size());
        char propBuf[PROPERTY_VALUE_MAX];
        property_get(prop, propBuf, NULL);
        val = strndup16to8(value.string(), value.size());

        return (strcmp(propBuf, val) == 0);
    }

    int parse_overlay_tag(const ResXMLTree& parser, const char *target_package_name,
            bool* is_static_overlay)
    {
        const size_t N = parser.getAttributeCount();
        String16 target;
        int priority = -1;
        String16 propName = String16();
        String16 propValue = String16();
        for (size_t i = 0; i < N; ++i) {
            size_t len;
            String16 key(parser.getAttributeName(i, &len));
@@ -109,34 +124,32 @@ namespace {
                if (parser.getAttributeValue(i, &v) == sizeof(Res_value)) {
                    *is_static_overlay = (v.data != 0);
                }
            } else if (key == String16("requiredSystemPropertyName")) {
                const char16_t *p = parser.getAttributeStringValue(i, &len);
                if (p != NULL) {
                    propName = String16(p, len);
                }
        }
        if (target == String16(target_package_name)) {
            return priority;
        }
        return NO_OVERLAY_TAG;
    }

    String16 parse_package_name(const ResXMLTree& parser)
    {
        const size_t N = parser.getAttributeCount();
        String16 package_name;
        for (size_t i = 0; i < N; ++i) {
            size_t len;
            String16 key(parser.getAttributeName(i, &len));
            if (key == String16("package")) {
            } else if (key == String16("requiredSystemPropertyValue")) {
                const char16_t *p = parser.getAttributeStringValue(i, &len);
                if (p != NULL) {
                    package_name = String16(p, len);
                    propValue = String16(p, len);
                }
            }
        }

        // Note that conditional property enablement/exclusion only applies if
        // the attribute is present. In its absence, all overlays are presumed enabled.
        if (propName.size() > 0 && propValue.size() > 0) {
            // if property set & equal to value, then include overlay - otherwise skip
            if (!check_property(propName, propValue)) {
                return NO_OVERLAY_TAG;
            }
        return package_name;
        }

    bool isValidStaticOverlayPackage(const String16& package_name) {
        // TODO(b/35742444): Need to support selection method based on a package name.
        return package_name.size() > 0;
        if (target == String16(target_package_name)) {
            return priority;
        }
        return NO_OVERLAY_TAG;
    }

    int parse_manifest(const void *data, size_t size, const char *target_package_name)
@@ -149,7 +162,6 @@ namespace {
        }

        ResXMLParser::event_code_t type;
        String16 package_name;
        bool is_static_overlay = false;
        int priority = NO_OVERLAY_TAG;
        do {
@@ -157,16 +169,14 @@ namespace {
            if (type == ResXMLParser::START_TAG) {
                size_t len;
                String16 tag(parser.getElementName(&len));
                if (tag == String16("manifest")) {
                    package_name = parse_package_name(parser);
                } else if (tag == String16("overlay")) {
                if (tag == String16("overlay")) {
                    priority = parse_overlay_tag(parser, target_package_name, &is_static_overlay);
                    break;
                }
            }
        } while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT);

        if (is_static_overlay && isValidStaticOverlayPackage(package_name)) {
        if (is_static_overlay) {
            return priority;
        }
        return NO_OVERLAY_TAG;
+35 −2
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ import android.os.FileUtils;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PatternMatcher;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.system.ErrnoException;
@@ -2111,6 +2112,12 @@ public class PackageParser {
                pkg.mIsStaticOverlay = sa.getBoolean(
                        com.android.internal.R.styleable.AndroidManifestResourceOverlay_isStatic,
                        false);
                final String propName = sa.getString(
                        com.android.internal.R.styleable
                        .AndroidManifestResourceOverlay_requiredSystemPropertyName);
                final String propValue = sa.getString(
                        com.android.internal.R.styleable
                        .AndroidManifestResourceOverlay_requiredSystemPropertyValue);
                sa.recycle();

                if (pkg.mOverlayTarget == null) {
@@ -2118,15 +2125,22 @@ public class PackageParser {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return null;
                }

                if (pkg.mOverlayPriority < 0 || pkg.mOverlayPriority > 9999) {
                    outError[0] = "<overlay> priority must be between 0 and 9999";
                    mParseError =
                        PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return null;
                }
                if (pkg.mIsStaticOverlay) {
                    // TODO(b/35742444): Need to support selection method based on a package name.

                // check to see if overlay should be excluded based on system property condition
                if (!checkOverlayRequiredSystemProperty(propName, propValue)) {
                    Slog.i(TAG, "Skipping target and overlay pair " + pkg.mOverlayTarget + " and "
                        + pkg.baseCodePath+ ": overlay ignored due to required system property: "
                        + propName + " with value: " + propValue);
                    return null;
                }

                XmlUtils.skipCurrentTag(parser);

            } else if (tagName.equals(TAG_KEY_SETS)) {
@@ -2531,6 +2545,25 @@ public class PackageParser {
        return pkg;
    }

    private boolean checkOverlayRequiredSystemProperty(String propName, String propValue) {

        if (TextUtils.isEmpty(propName) || TextUtils.isEmpty(propValue)) {
            if (!TextUtils.isEmpty(propName) || !TextUtils.isEmpty(propValue)) {
                // malformed condition - incomplete
                Slog.w(TAG, "Disabling overlay - incomplete property :'" + propName
                    + "=" + propValue + "' - require both requiredSystemPropertyName"
                    + " AND requiredSystemPropertyValue to be specified.");
                return false;
            }
            // no valid condition set - so no exclusion criteria, overlay will be included.
            return true;
        }

        // check property value - make sure it is both set and equal to expected value
        final String currValue = SystemProperties.get(propName);
        return (currValue != null && currValue.equals(propValue));
    }

    /**
     * This is a pre-density application which will get scaled - instead of being pixel perfect.
     * This type of application is not resizable.
+8 −0
Original line number Diff line number Diff line
@@ -2439,6 +2439,14 @@
        <!-- Whether the given RRO is static or not. -->
        <attr name="isStatic" format="boolean" />

        <!-- Required property name/value pair used to enable this overlay.
             e.g. name=ro.oem.sku value=MKT210.
             Overlay will be ignored unless system property exists and is
             set to specified value -->
        <!-- @hide @SystemApi This shouldn't be public. -->
        <attr name="requiredSystemPropertyName" format="string" />
        <!-- @hide @SystemApi This shouldn't be public. -->
        <attr name="requiredSystemPropertyValue" format="string" />
    </declare-styleable>

    <!-- Declaration of an {@link android.content.Intent} object in XML.  May
+4 −0
Original line number Diff line number Diff line
@@ -2817,6 +2817,10 @@
        <public name="defaultFocusHighlightEnabled" />
        <public name="persistentFeature"/>
        <public name="windowSplashscreenContent" />
        <!-- @hide @SystemApi -->
        <public name="requiredSystemPropertyName" />
        <!-- @hide @SystemApi -->
        <public name="requiredSystemPropertyValue" />
    </public-group>

    <public-group type="style" first-id="0x010302e0">
Loading