hiddenapi: Support 'core-platform-api' flag

Add support for parsing @CorePlatformApi stubs and encoding it in
hiddenapi dex flags of the corresponding fields/methods.

(1) The CL refactors hiddenapi::ApiList class to store a second value:
a bit vector of "domain API" flags. These are intended for encoding
membership in a set of API stubs only available to certain callers,
e.g. @CorePlatformApi when platform code calls core platform or
@TestApi for CTS tests.

(2) Parse @CorePlatformApi stubs and set domain flags for its members.

(3) Parse the flags at runtime and set kAccCorePlatformApi access flag
on the corresponding ArtField/ArtMethod objects.

Bug: 119068555
Test: m appcompat
Test: dexlayout -b <core-oj jar> | grep 'CORE-PLATFORM-API'
Change-Id: Idbfa6d3af7459258a5a0b6da7c03c037a577eb75
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index ef2c9e4..7382a97 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -224,7 +224,9 @@
 }
 
 static std::string GetHiddenapiFlagStr(uint32_t hiddenapi_flags) {
-  std::string api_list(hiddenapi::ApiList::FromDexFlags(hiddenapi_flags).GetName());
+  std::stringstream ss;
+  hiddenapi::ApiList(hiddenapi_flags).Dump(ss);
+  std::string api_list = ss.str();
   std::transform(api_list.begin(), api_list.end(), api_list.begin(), ::toupper);
   return api_list;
 }
diff --git a/libartbase/base/hiddenapi_flags.cc b/libartbase/base/hiddenapi_flags.cc
index 6caa75c..ea57cb7 100644
--- a/libartbase/base/hiddenapi_flags.cc
+++ b/libartbase/base/hiddenapi_flags.cc
@@ -19,7 +19,8 @@
 namespace art {
 namespace hiddenapi {
 
-constexpr const char* ApiList::kNames[ApiList::kValueCount];
+constexpr const char* ApiList::kValueNames[ApiList::kValueCount];
+constexpr const char* ApiList::kDomainApiNames[ApiList::kDomainApiCount];
 constexpr SdkVersion ApiList::kMaxSdkVersions[ApiList::kValueCount];
 
 }  // namespace hiddenapi
diff --git a/libartbase/base/hiddenapi_flags.h b/libartbase/base/hiddenapi_flags.h
index b898828..c468723 100644
--- a/libartbase/base/hiddenapi_flags.h
+++ b/libartbase/base/hiddenapi_flags.h
@@ -19,11 +19,52 @@
 
 #include "sdk_version.h"
 
+#include <vector>
+
 #include "android-base/logging.h"
+#include "base/bit_utils.h"
+#include "base/dumpable.h"
+#include "base/macros.h"
 
 namespace art {
 namespace hiddenapi {
 
+// Helper methods used inside ApiList. These were moved outside of the ApiList
+// class so that they can be used in static_asserts. If they were inside, they
+// would be part of an unfinished type.
+namespace helper {
+  // Casts enum value to uint32_t.
+  template<typename T>
+  constexpr uint32_t ToUint(T val) { return static_cast<uint32_t>(val); }
+
+  // Returns uint32_t with one bit set at an index given by an enum value.
+  template<typename T>
+  constexpr uint32_t ToBit(T val) { return 1u << ToUint(val); }
+
+  // Returns a bit mask with `size` least significant bits set.
+  constexpr uint32_t BitMask(uint32_t size) { return (1u << size) - 1; }
+
+  // Returns a bit mask formed from an enum defining kMin and kMax. The values
+  // are assumed to be indices of min/max bits and the resulting bitmask has
+  // bits [kMin, kMax] set.
+  template<typename T>
+  constexpr uint32_t BitMask() {
+    return BitMask(ToUint(T::kMax) + 1) & (~BitMask(ToUint(T::kMin)));
+  }
+
+  // Returns true if `val` is a bitwise subset of `mask`.
+  constexpr bool MatchesBitMask(uint32_t val, uint32_t mask) { return (val & mask) == val; }
+
+  // Returns true if the uint32_t value of `val` is a bitwise subset of `mask`.
+  template<typename T>
+  constexpr bool MatchesBitMask(T val, uint32_t mask) { return MatchesBitMask(ToUint(val), mask); }
+
+  // Returns the number of values defined in an enum, assuming the enum defines
+  // kMin and kMax and no integer values are skipped between them.
+  template<typename T>
+  constexpr uint32_t NumValues() { return ToUint(T::kMax) - ToUint(T::kMin) + 1; }
+}  // namespace helper
+
 /*
  * This class represents the information whether a field/method is in
  * public API (whitelist) or if it isn't, apps targeting which SDK
@@ -31,28 +72,55 @@
  */
 class ApiList {
  private:
-  using IntValueType = uint32_t;
+  // Number of bits reserved for Value in dex flags, and the corresponding bit mask.
+  static constexpr uint32_t kValueBitSize = 3;
+  static constexpr uint32_t kValueBitMask = helper::BitMask(kValueBitSize);
 
-  enum class Value : IntValueType {
+  enum class Value : uint32_t {
     // Values independent of target SDK version of app
-    kWhitelist =     0,
-    kGreylist =      1,
-    kBlacklist =     2,
+    kWhitelist =    0,
+    kGreylist =     1,
+    kBlacklist =    2,
 
     // Values dependent on target SDK version of app. Put these last as
     // their list will be extended in future releases.
     // The max release code implicitly includes all maintenance releases,
     // e.g. GreylistMaxO is accessible to targetSdkVersion <= 27 (O_MR1).
-    kGreylistMaxO =  3,
-    kGreylistMaxP =  4,
+    kGreylistMaxO = 3,
+    kGreylistMaxP = 4,
 
     // Special values
-    kInvalid =       static_cast<uint32_t>(-1),
-    kMinValue =      kWhitelist,
-    kMaxValue =      kGreylistMaxP,
+    kInvalid =      (static_cast<uint32_t>(-1) & kValueBitMask),
+    kMin =          kWhitelist,
+    kMax =          kGreylistMaxP,
   };
 
-  static constexpr const char* kNames[] = {
+  // Additional bit flags after the first kValueBitSize bits in dex flags.
+  // These are used for domain-specific API.
+  enum class DomainApi : uint32_t {
+    kCorePlatformApi = kValueBitSize,
+
+    // Special values
+    kMin =             kCorePlatformApi,
+    kMax =             kCorePlatformApi,
+  };
+
+  // Bit mask of all domain API flags.
+  static constexpr uint32_t kDomainApiBitMask = helper::BitMask<DomainApi>();
+
+  // Check that Values fit in the designated number of bits.
+  static_assert(kValueBitSize >= MinimumBitsToStore(helper::ToUint(Value::kMax)),
+                "Not enough bits to store all ApiList values");
+
+  // Sanity checks that all Values are covered by kValueBitMask.
+  static_assert(helper::MatchesBitMask(Value::kMin, kValueBitMask));
+  static_assert(helper::MatchesBitMask(Value::kMax, kValueBitMask));
+
+  // Assert that Value::kInvalid is larger than the maximum Value.
+  static_assert(helper::ToUint(Value::kMax) < helper::ToUint(Value::kInvalid));
+
+  // Names corresponding to Values.
+  static constexpr const char* kValueNames[] = {
     "whitelist",
     "greylist",
     "blacklist",
@@ -60,8 +128,12 @@
     "greylist-max-p",
   };
 
-  static constexpr const char* kInvalidName = "invalid";
+  // Names corresponding to DomainApis.
+  static constexpr const char* kDomainApiNames[] {
+    "core-platform-api",
+  };
 
+  // Maximum SDK versions allowed to access ApiList of given Value.
   static constexpr SdkVersion kMaxSdkVersions[] {
     /* whitelist */ SdkVersion::kMax,
     /* greylist */ SdkVersion::kMax,
@@ -70,76 +142,167 @@
     /* greylist-max-p */ SdkVersion::kP,
   };
 
-  static ApiList MinValue() { return ApiList(Value::kMinValue); }
-  static ApiList MaxValue() { return ApiList(Value::kMaxValue); }
+  explicit ApiList(Value val, uint32_t domain_apis = 0u)
+      : dex_flags_(helper::ToUint(val) | domain_apis) {
+    DCHECK(GetValue() == val);
+    DCHECK_EQ(GetDomainApis(), domain_apis);
+  }
 
-  explicit ApiList(Value value) : value_(value) {}
+  explicit ApiList(DomainApi val) : ApiList(Value::kInvalid, helper::ToBit(val)) {}
 
-  Value value_;
+  Value GetValue() const {
+    uint32_t value = (dex_flags_ & kValueBitMask);
+
+    // Treat all ones as invalid value
+    if (value == helper::ToUint(Value::kInvalid)) {
+      return Value::kInvalid;
+    } else {
+      DCHECK_GE(value, helper::ToUint(Value::kMin));
+      DCHECK_LE(value, helper::ToUint(Value::kMax));
+      return static_cast<Value>(value);
+    }
+  }
+
+  uint32_t GetDomainApis() const { return (dex_flags_ & kDomainApiBitMask); }
+
+  uint32_t dex_flags_;
 
  public:
   ApiList() : ApiList(Value::kInvalid) {}
 
+  explicit ApiList(uint32_t dex_flags) : dex_flags_(dex_flags) {
+    DCHECK_EQ(dex_flags_, (dex_flags_ & kValueBitMask) | (dex_flags_ & kDomainApiBitMask));
+  }
+
+  // Helpers for conveniently constructing ApiList instances.
   static ApiList Whitelist() { return ApiList(Value::kWhitelist); }
   static ApiList Greylist() { return ApiList(Value::kGreylist); }
   static ApiList Blacklist() { return ApiList(Value::kBlacklist); }
   static ApiList GreylistMaxO() { return ApiList(Value::kGreylistMaxO); }
   static ApiList GreylistMaxP() { return ApiList(Value::kGreylistMaxP); }
-  static ApiList Invalid() { return ApiList(Value::kInvalid); }
+  static ApiList CorePlatformApi() { return ApiList(DomainApi::kCorePlatformApi); }
 
-  // Decodes ApiList from dex hiddenapi flags.
-  static ApiList FromDexFlags(uint32_t dex_flags) {
-    if (MinValue().GetIntValue() <= dex_flags && dex_flags <= MaxValue().GetIntValue()) {
-      return ApiList(static_cast<Value>(dex_flags));
-    }
-    return Invalid();
-  }
+  uint32_t GetDexFlags() const { return dex_flags_; }
+  uint32_t GetIntValue() const { return helper::ToUint(GetValue()) - helper::ToUint(Value::kMin); }
 
-  // Decodes ApiList from its integer value.
-  static ApiList FromIntValue(IntValueType int_value) {
-    if (MinValue().GetIntValue() <= int_value && int_value <= MaxValue().GetIntValue()) {
-      return ApiList(static_cast<Value>(int_value));
-    }
-    return Invalid();
-  }
-
-  // Returns the ApiList with a given name.
+  // Returns the ApiList with a flag of a given name, or an empty ApiList if not matched.
   static ApiList FromName(const std::string& str) {
-    for (IntValueType i = MinValue().GetIntValue(); i <= MaxValue().GetIntValue(); i++) {
-      ApiList current = ApiList(static_cast<Value>(i));
-      if (str == current.GetName()) {
-        return current;
+    for (uint32_t i = 0; i < kValueCount; ++i) {
+      if (str == kValueNames[i]) {
+        return ApiList(static_cast<Value>(i + helper::ToUint(Value::kMin)));
       }
     }
-    return Invalid();
+    for (uint32_t i = 0; i < kDomainApiCount; ++i) {
+      if (str == kDomainApiNames[i]) {
+        return ApiList(static_cast<DomainApi>(i + helper::ToUint(DomainApi::kMin)));
+      }
+    }
+    return ApiList();
   }
 
-  bool operator==(const ApiList other) const { return value_ == other.value_; }
-  bool operator!=(const ApiList other) const { return !(*this == other); }
-
-  bool IsValid() const { return *this != Invalid(); }
-
-  IntValueType GetIntValue() const {
-    DCHECK(IsValid());
-    return static_cast<IntValueType>(value_);
+  // Parses a vector of flag names into a single ApiList value. If successful,
+  // returns true and assigns the new ApiList to `out_api_list`.
+  static bool FromNames(std::vector<std::string>::iterator begin,
+                        std::vector<std::string>::iterator end,
+                        /* out */ ApiList* out_api_list) {
+    ApiList api_list;
+    for (std::vector<std::string>::iterator it = begin; it != end; it++) {
+      ApiList current = FromName(*it);
+      if (current.IsEmpty() || !api_list.CanCombineWith(current)) {
+        return false;
+      }
+      api_list |= current;
+    }
+    if (out_api_list != nullptr) {
+      *out_api_list = api_list;
+    }
+    return true;
   }
 
-  const char* GetName() const { return IsValid() ? kNames[GetIntValue()]: kInvalidName; }
+  bool operator==(const ApiList& other) const { return dex_flags_ == other.dex_flags_; }
+  bool operator!=(const ApiList& other) const { return !(*this == other); }
 
+  // Returns true if combining this ApiList with `other` will succeed.
+  bool CanCombineWith(const ApiList& other) const {
+    const Value val1 = GetValue();
+    const Value val2 = other.GetValue();
+    return (val1 == val2) || (val1 == Value::kInvalid) || (val2 == Value::kInvalid);
+  }
+
+  // Combine two ApiList instances.
+  ApiList operator|(const ApiList& other) {
+    // DomainApis are not mutually exclusive. Simply OR them.
+    const uint32_t domain_apis = GetDomainApis() | other.GetDomainApis();
+
+    // Values are mutually exclusive. Check if `this` and `other` have the same Value
+    // or if at most one is set.
+    const Value val1 = GetValue();
+    const Value val2 = other.GetValue();
+    if (val1 == val2) {
+      return ApiList(val1, domain_apis);
+    } else if (val1 == Value::kInvalid) {
+      return ApiList(val2, domain_apis);
+    } else if (val2 == Value::kInvalid) {
+      return ApiList(val1, domain_apis);
+    } else {
+      LOG(FATAL) << "Invalid combination of values " << Dumpable(ApiList(val1))
+          << " and " << Dumpable(ApiList(val2));
+      UNREACHABLE();
+    }
+  }
+
+  const ApiList& operator|=(const ApiList& other) {
+    (*this) = (*this) | other;
+    return *this;
+  }
+
+  // Returns true if all flags set in `other` are also set in `this`.
+  bool Contains(const ApiList& other) const {
+    return ((other.GetValue() == Value::kInvalid) || (GetValue() == other.GetValue())) &&
+           helper::MatchesBitMask(other.GetDomainApis(), GetDomainApis());
+  }
+
+  // Returns true whether the configuration is valid for runtime use.
+  bool IsValid() const { return GetValue() != Value::kInvalid; }
+
+  // Returns true when no ApiList is specified and no domain_api flags either.
+  bool IsEmpty() const { return (GetValue() == Value::kInvalid) && (GetDomainApis() == 0); }
+
+  // Returns the maximum target SDK version allowed to access this ApiList.
   SdkVersion GetMaxAllowedSdkVersion() const { return kMaxSdkVersions[GetIntValue()]; }
 
-  static constexpr size_t kValueCount = static_cast<size_t>(Value::kMaxValue) + 1;
+  void Dump(std::ostream& os) const {
+    bool is_first = true;
+
+    if (GetValue() != Value::kInvalid) {
+      os << kValueNames[GetIntValue()];
+      is_first = false;
+    }
+
+    const uint32_t domain_apis = GetDomainApis();
+    for (uint32_t i = helper::ToUint(DomainApi::kMin); i <= helper::ToUint(DomainApi::kMax); i++) {
+      if (helper::MatchesBitMask(helper::ToBit(i), domain_apis)) {
+        if (is_first) {
+          is_first = false;
+        } else {
+          os << ",";
+        }
+        os << kDomainApiNames[i];
+      }
+    }
+
+    DCHECK_EQ(IsEmpty(), is_first);
+  }
+
+  static constexpr uint32_t kValueCount = helper::NumValues<Value>();
+  static constexpr uint32_t kDomainApiCount = helper::NumValues<DomainApi>();
 };
 
 inline std::ostream& operator<<(std::ostream& os, ApiList value) {
-  os << value.GetName();
+  value.Dump(os);
   return os;
 }
 
-inline bool AreValidDexFlags(uint32_t dex_flags) {
-  return ApiList::FromDexFlags(dex_flags).IsValid();
-}
-
 }  // namespace hiddenapi
 }  // namespace art
 
diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h
index 8562d05..f224a29 100644
--- a/libdexfile/dex/class_accessor-inl.h
+++ b/libdexfile/dex/class_accessor-inl.h
@@ -69,7 +69,7 @@
   code_off_ = DecodeUnsignedLeb128(&ptr_pos_);
   if (hiddenapi_ptr_pos_ != nullptr) {
     hiddenapi_flags_ = DecodeUnsignedLeb128(&hiddenapi_ptr_pos_);
-    DCHECK(hiddenapi::AreValidDexFlags(hiddenapi_flags_));
+    DCHECK(hiddenapi::ApiList(hiddenapi_flags_).IsValid());
   }
 }
 
@@ -83,7 +83,7 @@
   access_flags_ = DecodeUnsignedLeb128(&ptr_pos_);
   if (hiddenapi_ptr_pos_ != nullptr) {
     hiddenapi_flags_ = DecodeUnsignedLeb128(&hiddenapi_ptr_pos_);
-    DCHECK(hiddenapi::AreValidDexFlags(hiddenapi_flags_));
+    DCHECK(hiddenapi::ApiList(hiddenapi_flags_).IsValid());
   }
 }
 
diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc
index f376c4d..86a28e5 100644
--- a/libdexfile/dex/dex_file_verifier.cc
+++ b/libdexfile/dex/dex_file_verifier.cc
@@ -1630,7 +1630,7 @@
         failure = true;
         return;
       }
-      if (!hiddenapi::AreValidDexFlags(decoded_flags)) {
+      if (!hiddenapi::ApiList(decoded_flags).IsValid()) {
         ErrorStringPrintf("Hiddenapi class data flags invalid (%u) for %s %i",
                           decoded_flags, member_type, member.GetIndex());
         failure = true;
diff --git a/libdexfile/dex/modifiers.h b/libdexfile/dex/modifiers.h
index 114c8e6..0c79c96 100644
--- a/libdexfile/dex/modifiers.h
+++ b/libdexfile/dex/modifiers.h
@@ -85,7 +85,7 @@
 static constexpr uint32_t kAccSingleImplementation =  0x08000000;  // method (runtime)
 
 static constexpr uint32_t kAccPublicApi =             0x10000000;  // field, method
-static constexpr uint32_t kAccHiddenapiBits =         0x30000000;  // field, method
+static constexpr uint32_t kAccCorePlatformApi =       0x20000000;  // field, method
 
 // Non-intrinsics: Caches whether we can use fast-path in the interpreter invokes.
 // Intrinsics: These bits are part of the intrinsic ordinal.
@@ -102,6 +102,8 @@
 // class/ancestor overrides finalize()
 static constexpr uint32_t kAccClassIsFinalizable        = 0x80000000;
 
+static constexpr uint32_t kAccHiddenapiBits = kAccPublicApi | kAccCorePlatformApi;
+
 // Continuous sequence of bits used to hold the ordinal of an intrinsic method. Flags
 // which overlap are not valid when kAccIntrinsic is set.
 static constexpr uint32_t kAccIntrinsicBits = kAccHiddenapiBits |
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index e273d94..07193b2 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -727,7 +727,7 @@
     // these because (a) warnings on greylist do not change semantics, and
     // (b) only VarHandle intrinsics are blacklisted at the moment and they
     // should not be used outside tests with disabled API checks.
-    if ((hiddenapi_flags & kAccHiddenapiBits) == 0) {
+    if ((hiddenapi_flags & kAccHiddenapiBits) != kAccPublicApi) {
       DCHECK_EQ(hiddenapi_flags, hiddenapi::GetRuntimeFlags(this)) << PrettyMethod();
     }
   } else {
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index 2d3493d..adcf6b3 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -267,9 +267,6 @@
   }
 }
 
-static constexpr uint32_t kNoDexFlags = 0u;
-static constexpr uint32_t kInvalidDexFlags = static_cast<uint32_t>(-1);
-
 static ALWAYS_INLINE uint32_t GetMemberDexIndex(ArtField* field) {
   return field->GetDexFieldIndex();
 }
@@ -302,12 +299,10 @@
       ClassAccessor::Field, ClassAccessor::Method>::type;
 
   ObjPtr<mirror::Class> declaring_class = member->GetDeclaringClass();
-  if (declaring_class.IsNull()) {
-    return kNoDexFlags;
-  }
+  DCHECK(!declaring_class.IsNull()) << "Attempting to access a runtime method";
 
-  uint32_t flags = kInvalidDexFlags;
-  DCHECK(!AreValidDexFlags(flags));
+  ApiList flags;
+  DCHECK(!flags.IsValid());
 
   // Check if the declaring class has ClassExt allocated. If it does, check if
   // the pre-JVMTI redefine dex file has been set to determine if the declaring
@@ -318,17 +313,15 @@
     // Class is not redefined. Find the class def, iterate over its members and
     // find the entry corresponding to this `member`.
     const dex::ClassDef* class_def = declaring_class->GetClassDef();
-    if (class_def == nullptr) {
-      flags = kNoDexFlags;
-    } else {
-      uint32_t member_index = GetMemberDexIndex(member);
-      auto fn_visit = [&](const AccessorType& dex_member) {
-        if (dex_member.GetIndex() == member_index) {
-          flags = dex_member.GetHiddenapiFlags();
-        }
-      };
-      VisitMembers(declaring_class->GetDexFile(), *class_def, fn_visit);
-    }
+    DCHECK(class_def != nullptr) << "Class def should always be set for initialized classes";
+
+    uint32_t member_index = GetMemberDexIndex(member);
+    auto fn_visit = [&](const AccessorType& dex_member) {
+      if (dex_member.GetIndex() == member_index) {
+        flags = ApiList(dex_member.GetHiddenapiFlags());
+      }
+    };
+    VisitMembers(declaring_class->GetDexFile(), *class_def, fn_visit);
   } else {
     // Class was redefined using JVMTI. We have a pointer to the original dex file
     // and the class def index of this class in that dex file, but the field/method
@@ -344,16 +337,15 @@
       MemberSignature cur_signature(dex_member);
       if (member_signature.MemberNameAndTypeMatch(cur_signature)) {
         DCHECK(member_signature.Equals(cur_signature));
-        flags = dex_member.GetHiddenapiFlags();
+        flags = ApiList(dex_member.GetHiddenapiFlags());
       }
     };
     VisitMembers(*original_dex, original_class_def, fn_visit);
   }
 
-  CHECK_NE(flags, kInvalidDexFlags) << "Could not find hiddenapi flags for "
+  CHECK(flags.IsValid()) << "Could not find hiddenapi flags for "
       << Dumpable<MemberSignature>(MemberSignature(member));
-  DCHECK(AreValidDexFlags(flags));
-  return flags;
+  return flags.GetDexFlags();
 }
 
 template<typename T>
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 1a5e010..f085033 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -178,12 +178,17 @@
 ALWAYS_INLINE inline uint32_t CreateRuntimeFlags(const ClassAccessor::BaseItem& member) {
   uint32_t runtime_flags = 0u;
 
-  uint32_t dex_flags = member.GetHiddenapiFlags();
-  DCHECK(AreValidDexFlags(dex_flags));
+  ApiList api_list(member.GetHiddenapiFlags());
+  DCHECK(api_list.IsValid());
 
-  ApiList api_list = ApiList::FromDexFlags(dex_flags);
-  if (api_list == ApiList::Whitelist()) {
+  if (api_list.Contains(ApiList::Whitelist())) {
     runtime_flags |= kAccPublicApi;
+  } else {
+    // Only add domain-specific flags for non-public API members.
+    // This simplifies hardcoded values for intrinsics.
+    if (api_list.Contains(ApiList::CorePlatformApi())) {
+      runtime_flags |= kAccCorePlatformApi;
+    }
   }
 
   DCHECK_EQ(runtime_flags & kAccHiddenapiBits, runtime_flags)
@@ -325,7 +330,8 @@
   // Decode hidden API access flags from the dex file.
   // This is an O(N) operation scaling with the number of fields/methods
   // in the class. Only do this on slow path and only do it once.
-  ApiList api_list = ApiList::FromDexFlags(detail::GetDexFlags(member));
+  ApiList api_list(detail::GetDexFlags(member));
+  DCHECK(api_list.IsValid());
 
   // Member is hidden and caller is not exempted. Enter slow path.
   return detail::ShouldDenyAccessToMemberImpl(member, api_list, access_method);
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
index 49e5282..97fbcbf 100644
--- a/tools/hiddenapi/hiddenapi.cc
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -85,6 +85,7 @@
   UsageError("  Command \"list\": dump lists of public and private API");
   UsageError("    --boot-dex=<filename>: dex file which belongs to boot class path");
   UsageError("    --public-stub-classpath=<filenames>:");
+  UsageError("    --core-platform-stub-classpath=<filenames>:");
   UsageError("        colon-separated list of dex/apk files which form API stubs of boot");
   UsageError("        classpath. Multiple classpaths can be specified");
   UsageError("");
@@ -619,10 +620,10 @@
   // Append flags at the end of the data struct. This should be called
   // between BeginClassDef and EndClassDef in the order of appearance of
   // fields/methods in the class data stream.
-  void WriteFlags(ApiList flags) {
-    uint32_t uint_flags = flags.GetIntValue();
-    EncodeUnsignedLeb128(&data_, uint_flags);
-    class_def_has_non_zero_flags_ |= (uint_flags != 0u);
+  void WriteFlags(const ApiList& flags) {
+    uint32_t dex_flags = flags.GetDexFlags();
+    EncodeUnsignedLeb128(&data_, dex_flags);
+    class_def_has_non_zero_flags_ |= (dex_flags != 0u);
   }
 
   // Return backing data, assuming that all flags have been written.
@@ -927,6 +928,10 @@
             stub_classpaths_.push_back(std::make_pair(
                 option.substr(strlen("--public-stub-classpath=")).ToString(),
                 ApiList::Whitelist()));
+          } else if (option.starts_with("--core-platform-stub-classpath=")) {
+            stub_classpaths_.push_back(std::make_pair(
+                option.substr(strlen("--core-platform-stub-classpath=")).ToString(),
+                ApiList::CorePlatformApi()));
           } else if (option.starts_with("--out-api-flags=")) {
             api_flags_path_ = option.substr(strlen("--out-api-flags=")).ToString();
           } else {
@@ -1000,27 +1005,22 @@
 
     std::map<std::string, ApiList> api_flag_map;
 
-    int line_number = 1;
+    size_t line_number = 1;
     for (std::string line; std::getline(api_file, line); line_number++) {
       std::vector<std::string> values = android::base::Split(line, ",");
-      CHECK_GT(values.size(), 1u) << path << ":" << line_number << ": No flags found"
-          << kErrorHelp;
+      CHECK_GT(values.size(), 1u) << path << ":" << line_number
+          << ": No flags found: " << line << kErrorHelp;
+
       const std::string& signature = values[0];
+      CHECK(api_flag_map.find(signature) == api_flag_map.end()) << path << ":" << line_number
+          << ": Duplicate entry: " << signature << kErrorHelp;
 
-      CHECK(api_flag_map.find(signature) == api_flag_map.end()) << "Duplicate entry in " << path
-          << ": " << signature << kErrorHelp;
-
-      int numFlags = values.size() - 1;
-
-      CHECK_EQ(numFlags, 1) << "\n" << path << ":" << line_number << "\n"
-          << signature << ": Expected one flag, found " << numFlags << ":\n"
-          << ::android::base::Join(std::vector<std::string>(values.begin() + 1, values.end()), ",")
-          << kErrorHelp;
-
-      const std::string& flag_str = values[1];
-      hiddenapi::ApiList membership = hiddenapi::ApiList::FromName(flag_str);
-      CHECK(membership.IsValid()) << path << ":" << line_number << ": Unknown ApiList name: "
-          << flag_str << kErrorHelp;
+      ApiList membership;
+      bool success = ApiList::FromNames(values.begin() + 1, values.end(), &membership);
+      CHECK(success) << path << ":" << line_number
+          << ": Some flags were not recognized: " << line << kErrorHelp;
+      CHECK(membership.IsValid()) << path << ":" << line_number
+          << ": Invalid combination of flags: " << line << kErrorHelp;
 
       api_flag_map.emplace(signature, membership);
     }
@@ -1051,7 +1051,7 @@
 
     // Mark all boot dex members private.
     boot_classpath.ForEachDexMember([&](const DexMember& boot_member) {
-      boot_members[boot_member.GetApiEntry()] = ApiList::Invalid();
+      boot_members[boot_member.GetApiEntry()] = ApiList();
     });
 
     // Resolve each SDK dex member against the framework and mark it white.
@@ -1073,11 +1073,10 @@
                   std::string entry = boot_member.GetApiEntry();
                   auto it = boot_members.find(entry);
                   CHECK(it != boot_members.end());
-                  if (it->second.IsValid()) {
-                    CHECK_EQ(it->second, stub_api_list);
+                  if (it->second.Contains(stub_api_list)) {
                     return false;  // has been marked before
                   } else {
-                    it->second = stub_api_list;
+                    it->second |= stub_api_list;
                     return true;  // marked for the first time
                   }
                 });
@@ -1095,10 +1094,10 @@
     // Write into public/private API files.
     std::ofstream file_flags(api_flags_path_.c_str());
     for (const auto& entry : boot_members) {
-      if (entry.second.IsValid()) {
-        file_flags << entry.first << "," << entry.second << std::endl;
-      } else {
+      if (entry.second.IsEmpty()) {
         file_flags << entry.first << std::endl;
+      } else {
+        file_flags << entry.first << "," << entry.second << std::endl;
       }
     }
     file_flags.close();
diff --git a/tools/hiddenapi/hiddenapi_test.cc b/tools/hiddenapi/hiddenapi_test.cc
index 2689eed..7ef5b3d 100644
--- a/tools/hiddenapi/hiddenapi_test.cc
+++ b/tools/hiddenapi/hiddenapi_test.cc
@@ -138,7 +138,7 @@
         const uint32_t actual_visibility = field.GetAccessFlags() & kAccVisibilityFlags;
         CHECK_EQ(actual_visibility, expected_visibility)
             << "Field " << name << " in class " << accessor.GetDescriptor();
-        return hiddenapi::ApiList::FromDexFlags(field.GetHiddenapiFlags());
+        return hiddenapi::ApiList(field.GetHiddenapiFlags());
       }
     }
 
@@ -167,7 +167,7 @@
         const uint32_t actual_visibility = method.GetAccessFlags() & kAccVisibilityFlags;
         CHECK_EQ(actual_visibility, expected_visibility)
             << "Method " << name << " in class " << accessor.GetDescriptor();
-        return hiddenapi::ApiList::FromDexFlags(method.GetHiddenapiFlags());
+        return hiddenapi::ApiList(method.GetHiddenapiFlags());
       }
     }
 
diff --git a/tools/veridex/hidden_api.cc b/tools/veridex/hidden_api.cc
index 2af7b50..1dae93a 100644
--- a/tools/veridex/hidden_api.cc
+++ b/tools/veridex/hidden_api.cc
@@ -30,13 +30,12 @@
   std::ifstream in(filename);
   for (std::string str; std::getline(in, str);) {
     std::vector<std::string> values = android::base::Split(str, ",");
-    CHECK_EQ(values.size(), 2u) << "Currently only signature and one flag are supported";
-
     const std::string& signature = values[0];
-    const std::string& flag_str = values[1];
 
-    hiddenapi::ApiList membership = hiddenapi::ApiList::FromName(flag_str);
-    CHECK(membership.IsValid()) << "Unknown ApiList name: " << flag_str;
+    hiddenapi::ApiList membership;
+    bool success = hiddenapi::ApiList::FromNames(values.begin() + 1, values.end(), &membership);
+    CHECK(success) << "Unknown ApiList flag: " << str;
+    CHECK(membership.IsValid()) << "Invalid ApiList: " << membership;
 
     if (sdk_uses_only != (membership == hiddenapi::ApiList::Whitelist())) {
       // Either we want only SDK uses and this is not a whitelist entry,
diff --git a/tools/veridex/hidden_api.h b/tools/veridex/hidden_api.h
index d6a6438..3c7f29a 100644
--- a/tools/veridex/hidden_api.h
+++ b/tools/veridex/hidden_api.h
@@ -37,11 +37,11 @@
 
   hiddenapi::ApiList GetApiList(const std::string& name) const {
     auto it = api_list_.find(name);
-    return (it == api_list_.end()) ? hiddenapi::ApiList::Invalid() : it->second;
+    return (it == api_list_.end()) ? hiddenapi::ApiList() : it->second;
   }
 
   bool IsInAnyList(const std::string& name) const {
-    return GetApiList(name).IsValid();
+    return GetApiList(name).IsEmpty();
   }
 
   static std::string GetApiMethodName(const DexFile& dex_file, uint32_t method_index);
diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc
index 46ab8aa..3b6c7f9 100644
--- a/tools/veridex/veridex.cc
+++ b/tools/veridex/veridex.cc
@@ -255,7 +255,7 @@
          << stats.linking_count << " linked against, "
          << stats.reflection_count << " through reflection" << std::endl;
       for (size_t i = 0; i < hiddenapi::ApiList::kValueCount; ++i) {
-        hiddenapi::ApiList api_list = hiddenapi::ApiList::FromIntValue(i);
+        hiddenapi::ApiList api_list = hiddenapi::ApiList(i);
         if (api_list != hiddenapi::ApiList::Whitelist()) {
           os << kPrefix << stats.api_counts[i] << " in " << api_list << std::endl;
         }