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

Commit 833f3ccb authored by Adam Lesinski's avatar Adam Lesinski
Browse files

AAPT support for feature splits

This change allows the developer to add a base package for
which to build a feature split. The generated resource types
will begin after the base APK's defined types so as not
to collide or override resources.

Multiple features can be generated by first choosing an
arbitrary order for the features. Then for each feature,
the base APK and any preceding features are specified
with the --feature-of flags.

So with a base APK 'A' and features, 'B', and 'C',
'B' would be built with

aapt package [...] --feature-of A [...]

and 'C' would be built with

aapt package [...] --feature-of A --feature-of B [...]

Change-Id: I1be66e3f8df9a737b21c71f8a93685376c7e6780
parent 5c09e8ad
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -1684,6 +1684,8 @@ public:
    public:
        inline virtual ~Accessor() { }

        virtual const String16& getAssetsPackage() const = 0;

        virtual uint32_t getCustomResource(const String16& package,
                                           const String16& type,
                                           const String16& name) const = 0;
+6 −4
Original line number Diff line number Diff line
@@ -4630,6 +4630,7 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString,
                    type.size(), package.string(), package.size(), &specFlags);
            if (rid != 0) {
                if (enforcePrivate) {
                    if (accessor == NULL || accessor->getAssetsPackage() != package) {
                        if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) {
                            if (accessor != NULL) {
                                accessor->reportError(accessorCookie, "Resource is not public.");
@@ -4637,6 +4638,7 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString,
                            return false;
                        }
                    }
                }

                if (accessor) {
                    rid = Res_MAKEID(
+4 −13
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <utils/String8.h>
#include <utils/String16.h>
#include "TestHelpers.h"
#include "data/R.h"

#include <gtest/gtest.h>

@@ -43,16 +44,6 @@ namespace {

enum { MAY_NOT_BE_BAG = false };

static const uint32_t attr_attr1            = 0x7f010000;
static const uint32_t attr_attr2            = 0x7f010001;
static const uint32_t string_test1          = 0x7f020000;
static const uint32_t string_test2          = 0x7f020001;
static const uint32_t integer_number1       = 0x7f030000;
static const uint32_t integer_number2       = 0x7f030001;
static const uint32_t style_Theme1          = 0x7f040000;
static const uint32_t style_Theme2          = 0x7f040001;
static const uint32_t array_integerArray1   = 0x7f050000;

class IdmapTest : public ::testing::Test {
protected:
    virtual void SetUp() {
@@ -79,7 +70,7 @@ TEST_F(IdmapTest, canLoadIdmap) {

TEST_F(IdmapTest, overlayOverridesResourceValue) {
    Res_value val;
    ssize_t block = mTargetTable.getResource(string_test2, &val, false);
    ssize_t block = mTargetTable.getResource(R::string::test2, &val, false);
    ASSERT_GE(block, 0);
    ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
    const ResStringPool* pool = mTargetTable.getTableStringBlock(block);
@@ -93,7 +84,7 @@ TEST_F(IdmapTest, overlayOverridesResourceValue) {

    ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize));

    ssize_t newBlock = mTargetTable.getResource(string_test2, &val, false);
    ssize_t newBlock = mTargetTable.getResource(R::string::test2, &val, false);
    ASSERT_GE(newBlock, 0);
    ASSERT_NE(block, newBlock);
    ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
@@ -110,7 +101,7 @@ TEST_F(IdmapTest, overlaidResourceHasSameName) {
    ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize));

    ResTable::resource_name resName;
    ASSERT_TRUE(mTargetTable.getResourceName(array_integerArray1, false, &resName));
    ASSERT_TRUE(mTargetTable.getResourceName(R::array::integerArray1, false, &resName));

    ASSERT_TRUE(resName.package != NULL);
    ASSERT_TRUE(resName.type != NULL);
+20 −29
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <utils/String8.h>
#include <utils/String16.h>
#include "TestHelpers.h"
#include "data/R.h"

#include <gtest/gtest.h>

@@ -35,16 +36,6 @@ namespace {

enum { MAY_NOT_BE_BAG = false };

static const uint32_t attr_attr1            = 0x7f010000;
static const uint32_t attr_attr2            = 0x7f010001;
static const uint32_t string_test1          = 0x7f020000;
static const uint32_t string_test2          = 0x7f020001;
static const uint32_t integer_number1       = 0x7f030000;
static const uint32_t integer_number2       = 0x7f030001;
static const uint32_t style_Theme1          = 0x7f040000;
static const uint32_t style_Theme2          = 0x7f040001;
static const uint32_t array_integerArray1   = 0x7f050000;

TEST(ResTableTest, shouldLoadSuccessfully) {
    ResTable table;
    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
@@ -55,7 +46,7 @@ TEST(ResTableTest, simpleTypeIsRetrievedCorrectly) {
    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));

    Res_value val;
    ssize_t block = table.getResource(string_test1, &val, MAY_NOT_BE_BAG);
    ssize_t block = table.getResource(R::string::test1, &val, MAY_NOT_BE_BAG);

    ASSERT_GE(block, 0);
    ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
@@ -75,7 +66,7 @@ TEST(ResTableTest, resourceNameIsResolved) {
                                             0, 0,
                                             defPackage.string(), defPackage.size());
    ASSERT_NE(uint32_t(0x00000000), resID);
    ASSERT_EQ(string_test1, resID);
    ASSERT_EQ(R::string::test1, resID);
}

TEST(ResTableTest, noParentThemeIsAppliedCorrectly) {
@@ -83,19 +74,19 @@ TEST(ResTableTest, noParentThemeIsAppliedCorrectly) {
    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));

    ResTable::Theme theme(table);
    ASSERT_EQ(NO_ERROR, theme.applyStyle(style_Theme1));
    ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::Theme1));

    Res_value val;
    uint32_t specFlags = 0;
    ssize_t index = theme.getAttribute(attr_attr1, &val, &specFlags);
    ssize_t index = theme.getAttribute(R::attr::attr1, &val, &specFlags);
    ASSERT_GE(index, 0);
    ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
    ASSERT_EQ(uint32_t(100), val.data);

    index = theme.getAttribute(attr_attr2, &val, &specFlags);
    index = theme.getAttribute(R::attr::attr2, &val, &specFlags);
    ASSERT_GE(index, 0);
    ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
    ASSERT_EQ(integer_number1, val.data);
    ASSERT_EQ(R::integer::number1, val.data);
}

TEST(ResTableTest, parentThemeIsAppliedCorrectly) {
@@ -103,19 +94,19 @@ TEST(ResTableTest, parentThemeIsAppliedCorrectly) {
    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));

    ResTable::Theme theme(table);
    ASSERT_EQ(NO_ERROR, theme.applyStyle(style_Theme2));
    ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::Theme2));

    Res_value val;
    uint32_t specFlags = 0;
    ssize_t index = theme.getAttribute(attr_attr1, &val, &specFlags);
    ssize_t index = theme.getAttribute(R::attr::attr1, &val, &specFlags);
    ASSERT_GE(index, 0);
    ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
    ASSERT_EQ(uint32_t(300), val.data);

    index = theme.getAttribute(attr_attr2, &val, &specFlags);
    index = theme.getAttribute(R::attr::attr2, &val, &specFlags);
    ASSERT_GE(index, 0);
    ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
    ASSERT_EQ(integer_number1, val.data);
    ASSERT_EQ(R::integer::number1, val.data);
}

TEST(ResTableTest, referenceToBagIsNotResolved) {
@@ -123,15 +114,15 @@ TEST(ResTableTest, referenceToBagIsNotResolved) {
    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));

    Res_value val;
    ssize_t block = table.getResource(integer_number2, &val, MAY_NOT_BE_BAG);
    ssize_t block = table.getResource(R::integer::number2, &val, MAY_NOT_BE_BAG);
    ASSERT_GE(block, 0);
    ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
    ASSERT_EQ(array_integerArray1, val.data);
    ASSERT_EQ(R::array::integerArray1, val.data);

    ssize_t newBlock = table.resolveReference(&val, block);
    EXPECT_EQ(block, newBlock);
    EXPECT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
    EXPECT_EQ(array_integerArray1, val.data);
    EXPECT_EQ(R::array::integerArray1, val.data);
}

TEST(ResTableTest, resourcesStillAccessibleAfterParameterChange) {
@@ -139,12 +130,12 @@ TEST(ResTableTest, resourcesStillAccessibleAfterParameterChange) {
    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));

    Res_value val;
    ssize_t block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG);
    ssize_t block = table.getResource(R::integer::number1, &val, MAY_NOT_BE_BAG);
    ASSERT_GE(block, 0);
    ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);

    const ResTable::bag_entry* entry;
    ssize_t count = table.lockBag(array_integerArray1, &entry);
    ssize_t count = table.lockBag(R::array::integerArray1, &entry);
    ASSERT_GE(count, 0);
    table.unlockBag(entry);

@@ -153,11 +144,11 @@ TEST(ResTableTest, resourcesStillAccessibleAfterParameterChange) {
    param.density = 320;
    table.setParameters(&param);

    block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG);
    block = table.getResource(R::integer::number1, &val, MAY_NOT_BE_BAG);
    ASSERT_GE(block, 0);
    ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);

    count = table.lockBag(array_integerArray1, &entry);
    count = table.lockBag(R::array::integerArray1, &entry);
    ASSERT_GE(count, 0);
    table.unlockBag(entry);
}
@@ -167,7 +158,7 @@ TEST(ResTableTest, resourceIsOverridenWithBetterConfig) {
    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));

    Res_value val;
    ssize_t block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG);
    ssize_t block = table.getResource(R::integer::number1, &val, MAY_NOT_BE_BAG);
    ASSERT_GE(block, 0);
    ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
    ASSERT_EQ(uint32_t(200), val.data);
@@ -180,7 +171,7 @@ TEST(ResTableTest, resourceIsOverridenWithBetterConfig) {
    param.country[1] = 'E';
    table.setParameters(&param);

    block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG);
    block = table.getResource(R::integer::number1, &val, MAY_NOT_BE_BAG);
    ASSERT_GE(block, 0);
    ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
    ASSERT_EQ(uint32_t(400), val.data);
+66 −36
Original line number Diff line number Diff line
@@ -19,46 +19,37 @@
#include <utils/String8.h>
#include <utils/String16.h>
#include "TestHelpers.h"
#include "data/R.h"

#include <gtest/gtest.h>

using namespace android;

namespace {

/**
 * Include a binary resource table. This table
 * is a base table for an APK split.
 *
 * Package: com.android.example.split
 *
 * layout/main          0x7f020000 {default, fr-sw600dp-v13}
 *
 * string/app_title     0x7f030000 {default}
 * string/test          0x7f030001 {default}
 * string/boom          0x7f030002 {default}
 * string/blah          0x7f030003 {default}
 *
 * array/lotsofstrings  0x7f040000 {default}
 * array/numList        0x7f040001 {default}
 * array/ary            0x7f040002 {default}
 *
 * Package: com.android.test.basic
 */
#include "data/split_base_arsc.h"
#include "data/basic/basic_arsc.h"

/**
 * Include a binary resource table. This table
 * is a configuration split table for an APK split.
 *
 * Package: com.android.example.split
 *
 * string/app_title     0x7f030000 {fr}
 * string/test          0x7f030001 {de,fr}
 * string/blah          0x7f030003 {fr}
 *
 * array/lotsofstrings  0x7f040000 {fr}
 *
 * Package: com.android.test.basic
 */
#include "data/split_de_fr_arsc.h"

#include "data/basic/split_de_fr_arsc.h"

using namespace android;
/**
 * Include a binary resource table. This table
 * is a feature split table for an APK split.
 *
 * Package: com.android.test.basic
 */
#include "data/feature/feature_arsc.h"

enum { MAY_NOT_BE_BAG = false };

@@ -70,7 +61,7 @@ void makeConfigFrench(ResTable_config* config) {

TEST(SplitTest, TestLoadBase) {
    ResTable table;
    ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len));
    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
}

TEST(SplitTest, TestGetResourceFromBase) {
@@ -80,14 +71,14 @@ TEST(SplitTest, TestGetResourceFromBase) {
    ResTable table;
    table.setParameters(&frenchConfig);

    ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len));
    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));

    ResTable_config expectedConfig;
    memset(&expectedConfig, 0, sizeof(expectedConfig));

    Res_value val;
    ResTable_config config;
    ssize_t block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, NULL, &config);
    ssize_t block = table.getResource(R::string::test1, &val, MAY_NOT_BE_BAG, 0, NULL, &config);

    // The returned block should tell us which string pool to get the value, if it is a string.
    EXPECT_GE(block, 0);
@@ -105,12 +96,12 @@ TEST(SplitTest, TestGetResourceFromSplit) {
    ResTable table;
    table.setParameters(&expectedConfig);

    ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len));
    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
    ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len));

    Res_value val;
    ResTable_config config;
    ssize_t block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, NULL, &config);
    ssize_t block = table.getResource(R::string::test1, &val, MAY_NOT_BE_BAG, 0, NULL, &config);

    EXPECT_GE(block, 0);

@@ -126,15 +117,15 @@ TEST(SplitTest, ResourcesFromBaseAndSplitHaveSameNames) {
    ResTable table;
    table.setParameters(&expectedConfig);

    ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len));
    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));

    ResTable::resource_name baseName;
    EXPECT_TRUE(table.getResourceName(0x7f030003, false, &baseName));
    EXPECT_TRUE(table.getResourceName(R::string::test1, false, &baseName));

    ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len));

    ResTable::resource_name frName;
    EXPECT_TRUE(table.getResourceName(0x7f030003, false, &frName));
    EXPECT_TRUE(table.getResourceName(R::string::test1, false, &frName));

    EXPECT_EQ(
            String16(baseName.package, baseName.packageLen),
@@ -154,11 +145,11 @@ TEST(SplitTest, TypeEntrySpecFlagsAreUpdated) {
    memset(&defaultConfig, 0, sizeof(defaultConfig));

    ResTable table;
    ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len));
    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));

    Res_value val;
    uint32_t specFlags = 0;
    ssize_t block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, &specFlags, NULL);
    ssize_t block = table.getResource(R::string::test1, &val, MAY_NOT_BE_BAG, 0, &specFlags, NULL);
    EXPECT_GE(block, 0);

    EXPECT_EQ(static_cast<uint32_t>(0), specFlags);
@@ -166,8 +157,47 @@ TEST(SplitTest, TypeEntrySpecFlagsAreUpdated) {
    ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len));

    uint32_t frSpecFlags = 0;
    block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, &frSpecFlags, NULL);
    block = table.getResource(R::string::test1, &val, MAY_NOT_BE_BAG, 0, &frSpecFlags, NULL);
    EXPECT_GE(block, 0);

    EXPECT_EQ(ResTable_config::CONFIG_LOCALE, frSpecFlags);
}

TEST(SplitFeatureTest, TestNewResourceIsAccessible) {
    ResTable table;
    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));

    Res_value val;
    ssize_t block = table.getResource(R::string::test3, &val, MAY_NOT_BE_BAG);
    EXPECT_LT(block, 0);

    ASSERT_EQ(NO_ERROR, table.add(feature_arsc, feature_arsc_len));

    block = table.getResource(R::string::test3, &val, MAY_NOT_BE_BAG);
    EXPECT_GE(block, 0);

    EXPECT_EQ(Res_value::TYPE_STRING, val.dataType);
}

TEST(SplitFeatureTest, TestNewResourceIsAccessibleByName) {
    ResTable table;
    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));

    ResTable::resource_name name;
    EXPECT_FALSE(table.getResourceName(R::string::test3, false, &name));

    ASSERT_EQ(NO_ERROR, table.add(feature_arsc, feature_arsc_len));

    EXPECT_TRUE(table.getResourceName(R::string::test3, false, &name));

    EXPECT_EQ(String16("com.android.test.basic"),
            String16(name.package, name.packageLen));

    EXPECT_EQ(String16("string"),
            String16(name.type, name.typeLen));

    EXPECT_EQ(String16("test3"),
            String16(name.name, name.nameLen));
}

} // namespace
Loading