LoadedArsc: Support feature splits.

Test: make libandroidfw_tests
Change-Id: I278273e688da597f4af86dd55f87750501ef8154
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index e17a3a6..c7d0fa5 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -116,7 +116,10 @@
                               LoadedArscEntry* out_entry, ResTable_config* out_selected_config,
                               uint32_t* out_flags) const {
   ATRACE_CALL();
-  const TypeSpecPtr& ptr = type_specs_[type_idx];
+
+  // If the type IDs are offset in this package, we need to take that into account when searching
+  // for a type.
+  const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_];
   if (ptr == nullptr) {
     return false;
   }
@@ -334,6 +337,15 @@
     loaded_package->dynamic_ = true;
   }
 
+  if (header->header.headerSize >= sizeof(ResTable_package)) {
+    uint32_t type_id_offset = dtohl(header->typeIdOffset);
+    if (type_id_offset > std::numeric_limits<uint8_t>::max()) {
+      LOG(ERROR) << "Type ID offset in RES_TABLE_PACKAGE_TYPE is too large.";
+      return {};
+    }
+    loaded_package->type_id_offset_ = static_cast<int>(type_id_offset);
+  }
+
   util::ReadUtf16StringFromDevice(header->name, arraysize(header->name),
                                   &loaded_package->package_name_);
 
@@ -385,7 +397,6 @@
             LOG(ERROR) << "Too many type configurations, overflow detected.";
             return {};
           }
-
           loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr);
 
           types_builder = {};
@@ -403,6 +414,12 @@
           return {};
         }
 
+        if (loaded_package->type_id_offset_ + static_cast<int>(type_spec->id) >
+            std::numeric_limits<uint8_t>::max()) {
+          LOG(ERROR) << "Chunk RES_TABLE_TYPE_SPEC_TYPE has out of range ID.";
+          return {};
+        }
+
         // The data portion of this chunk contains entry_count 32bit entries,
         // each one representing a set of flags.
         // Here we only validate that the chunk is well formed.
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 8362008..e8cb164 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -94,6 +94,7 @@
   std::string package_name_;
   int package_id_ = -1;
   bool dynamic_ = false;
+  int type_id_offset_ = 0;
 
   ByteBucketArray<util::unique_cptr<TypeSpec>> type_specs_;
   std::vector<DynamicPackageEntry> dynamic_package_map_;
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 045507e..f8aa61a 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -139,6 +139,36 @@
   EXPECT_EQ(0x7f, packages[0]->GetPackageId());
 }
 
+TEST(LoadedArscTest, LoadFeatureSplit) {
+  std::string contents;
+  ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc",
+                                      &contents));
+  std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
+  ASSERT_NE(nullptr, loaded_arsc);
+
+  ResTable_config desired_config;
+  memset(&desired_config, 0, sizeof(desired_config));
+
+  LoadedArscEntry entry;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry,
+                                     &selected_config, &flags));
+
+  size_t len;
+  const char16_t* type_name16 = entry.type_string_ref.string16(&len);
+  ASSERT_NE(nullptr, type_name16);
+  ASSERT_NE(0u, len);
+
+  size_t utf8_len = utf16_to_utf8_length(type_name16, len);
+  std::string type_name;
+  type_name.resize(utf8_len);
+  utf16_to_utf8(type_name16, len, &*type_name.begin(), utf8_len + 1);
+
+  EXPECT_EQ(std::string("string"), type_name);
+}
+
 // structs with size fields (like Res_value, ResTable_entry) should be
 // backwards and forwards compatible (aka checking the size field against
 // sizeof(Res_value) might not be backwards compatible.
diff --git a/libs/androidfw/tests/data/feature/feature.apk b/libs/androidfw/tests/data/feature/feature.apk
index 04940fb..767fed6 100644
--- a/libs/androidfw/tests/data/feature/feature.apk
+++ b/libs/androidfw/tests/data/feature/feature.apk
Binary files differ