Merge changes from topics "coreapi-flag", "coreapi-stubs-csv"

* changes:
  hiddenapi: Print warnings for @CorePlatformApi violations
  hiddenapi: Support 'core-platform-api' flag
  hiddenapi: Produce CSV for `list` command
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_domain.h b/libartbase/base/hiddenapi_domain.h
new file mode 100644
index 0000000..4cbc22d
--- /dev/null
+++ b/libartbase/base/hiddenapi_domain.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBARTBASE_BASE_HIDDENAPI_DOMAIN_H_
+#define ART_LIBARTBASE_BASE_HIDDENAPI_DOMAIN_H_
+
+namespace art {
+namespace hiddenapi {
+
+// List of domains supported by the hidden API access checks. Domain with a lower
+// ordinal is considered more "trusted", i.e. always allowed to access members of
+// domains with a greater ordinal. Access checks are performed when code tries to
+// access a method/field from a more trusted domain than itself.
+enum class Domain {
+  kCorePlatform = 0,
+  kPlatform,
+  kApplication,
+};
+
+inline bool IsDomainMoreTrustedThan(Domain domainA, Domain domainB) {
+  return static_cast<uint32_t>(domainA) <= static_cast<uint32_t>(domainB);
+}
+
+}  // namespace hiddenapi
+}  // namespace art
+
+
+#endif  // ART_LIBARTBASE_BASE_HIDDENAPI_DOMAIN_H_
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 8c1ffd5..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,74 +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/art_dex_file_loader.cc b/libdexfile/dex/art_dex_file_loader.cc
index 57e838f..a814b66 100644
--- a/libdexfile/dex/art_dex_file_loader.cc
+++ b/libdexfile/dex/art_dex_file_loader.cc
@@ -547,7 +547,7 @@
   // that this will call `realpath`.
   std::string path = DexFileLoader::GetDexCanonicalLocation(location.c_str());
   if (dex_file != nullptr && LocationIsOnSystemFramework(path.c_str())) {
-    dex_file->SetIsPlatformDexFile();
+    dex_file->SetHiddenapiDomain(hiddenapi::Domain::kPlatform);
   }
 
   return dex_file;
diff --git a/libdexfile/dex/art_dex_file_loader_test.cc b/libdexfile/dex/art_dex_file_loader_test.cc
index f9516db..8c9258b 100644
--- a/libdexfile/dex/art_dex_file_loader_test.cc
+++ b/libdexfile/dex/art_dex_file_loader_test.cc
@@ -340,7 +340,7 @@
 
   ASSERT_GE(dex_files.size(), 1u);
   for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
-    ASSERT_FALSE(dex_file->IsPlatformDexFile());
+    ASSERT_NE(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
   }
 
   dex_files.clear();
@@ -368,7 +368,7 @@
 
   ASSERT_GE(dex_files.size(), 1u);
   for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
-    ASSERT_FALSE(dex_file->IsPlatformDexFile());
+    ASSERT_NE(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
   }
 
   dex_files.clear();
@@ -396,7 +396,7 @@
 
   ASSERT_GE(dex_files.size(), 1u);
   for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
-    ASSERT_TRUE(dex_file->IsPlatformDexFile());
+    ASSERT_EQ(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
   }
 
   dex_files.clear();
@@ -424,7 +424,7 @@
 
   ASSERT_GT(dex_files.size(), 1u);
   for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
-    ASSERT_FALSE(dex_file->IsPlatformDexFile());
+    ASSERT_NE(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
   }
 
   dex_files.clear();
@@ -453,7 +453,7 @@
 
   ASSERT_GT(dex_files.size(), 1u);
   for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
-    ASSERT_FALSE(dex_file->IsPlatformDexFile());
+    ASSERT_NE(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
   }
 
   dex_files.clear();
@@ -482,7 +482,7 @@
 
   ASSERT_GT(dex_files.size(), 1u);
   for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
-    ASSERT_TRUE(dex_file->IsPlatformDexFile());
+    ASSERT_EQ(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
   }
 
   dex_files.clear();
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.cc b/libdexfile/dex/dex_file.cc
index d3cdf13..39377a3 100644
--- a/libdexfile/dex/dex_file.cc
+++ b/libdexfile/dex/dex_file.cc
@@ -123,7 +123,7 @@
       oat_dex_file_(oat_dex_file),
       container_(std::move(container)),
       is_compact_dex_(is_compact_dex),
-      is_platform_dex_(false) {
+      hiddenapi_domain_(hiddenapi::Domain::kApplication) {
   CHECK(begin_ != nullptr) << GetLocation();
   CHECK_GT(size_, 0U) << GetLocation();
   // Check base (=header) alignment.
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index a940a66..c7fbe78 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -24,6 +24,7 @@
 #include <android-base/logging.h>
 
 #include "base/globals.h"
+#include "base/hiddenapi_domain.h"
 #include "base/macros.h"
 #include "base/value_object.h"
 #include "class_iterator.h"
@@ -754,13 +755,8 @@
   ALWAYS_INLINE const StandardDexFile* AsStandardDexFile() const;
   ALWAYS_INLINE const CompactDexFile* AsCompactDexFile() const;
 
-  ALWAYS_INLINE bool IsPlatformDexFile() const {
-    return is_platform_dex_;
-  }
-
-  ALWAYS_INLINE void SetIsPlatformDexFile() {
-    is_platform_dex_ = true;
-  }
+  hiddenapi::Domain GetHiddenapiDomain() const { return hiddenapi_domain_; }
+  void SetHiddenapiDomain(hiddenapi::Domain value) { hiddenapi_domain_ = value; }
 
   bool IsInMainSection(const void* addr) const {
     return Begin() <= addr && addr < Begin() + Size();
@@ -874,8 +870,7 @@
   // If the dex file is a compact dex file. If false then the dex file is a standard dex file.
   const bool is_compact_dex_;
 
-  // If the dex file is located in /system/framework/.
-  bool is_platform_dex_;
+  hiddenapi::Domain hiddenapi_domain_;
 
   friend class DexFileLoader;
   friend class DexFileVerifierTest;
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/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc
index 41e4291..a3e06e6 100644
--- a/openjdkjvmti/fixed_up_dex_file.cc
+++ b/openjdkjvmti/fixed_up_dex_file.cc
@@ -141,14 +141,12 @@
       /*verify_checksum=*/false,
       &error);
 
-  if (new_dex_file  == nullptr) {
+  if (new_dex_file == nullptr) {
     LOG(ERROR) << "Unable to open dex file from memory for unquickening! error: " << error;
     return nullptr;
   }
 
-  if (original.IsPlatformDexFile()) {
-    const_cast<art::DexFile*>(new_dex_file.get())->SetIsPlatformDexFile();
-  }
+  const_cast<art::DexFile*>(new_dex_file.get())->SetHiddenapiDomain(original.GetHiddenapiDomain());
 
   DoDexUnquicken(*new_dex_file, original);
 
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..af5e67a 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -67,6 +67,19 @@
   return os;
 }
 
+static inline std::ostream& operator<<(std::ostream& os, const AccessContext& value)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (!value.GetClass().IsNull()) {
+    std::string tmp;
+    os << value.GetClass()->GetDescriptor(&tmp);
+  } else if (value.GetDexFile() != nullptr) {
+    os << value.GetDexFile()->GetLocation();
+  } else {
+    os << "<unknown_caller>";
+  }
+  return os;
+}
+
 namespace detail {
 
 // Do not change the values of items in this enum, as they are written to the
@@ -267,9 +280,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 +312,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 +326,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,22 +350,30 @@
       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>
-bool ShouldDenyAccessToMemberImpl(T* member,
-                                  hiddenapi::ApiList api_list,
-                                  AccessMethod access_method) {
+void MaybeReportCorePlatformApiViolation(T* member,
+                                         const AccessContext& caller_context,
+                                         AccessMethod access_method) {
+  if (access_method != AccessMethod::kNone) {
+    MemberSignature sig(member);
+    LOG(ERROR) << "CorePlatformApi violation: " << Dumpable<MemberSignature>(sig)
+               << " from " << caller_context << " using " << access_method;
+  }
+}
+
+template<typename T>
+bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method) {
   DCHECK(member != nullptr);
   Runtime* runtime = Runtime::Current();
 
@@ -414,14 +428,20 @@
   return deny_access;
 }
 
-// Need to instantiate this.
+// Need to instantiate these.
 template uint32_t GetDexFlags<ArtField>(ArtField* member);
 template uint32_t GetDexFlags<ArtMethod>(ArtMethod* member);
+template void MaybeReportCorePlatformApiViolation(ArtField* member,
+                                                  const AccessContext& caller_context,
+                                                  AccessMethod access_method);
+template void MaybeReportCorePlatformApiViolation(ArtMethod* member,
+                                                  const AccessContext& caller_context,
+                                                  AccessMethod access_method);
 template bool ShouldDenyAccessToMemberImpl<ArtField>(ArtField* member,
-                                                     hiddenapi::ApiList api_list,
+                                                     ApiList api_list,
                                                      AccessMethod access_method);
 template bool ShouldDenyAccessToMemberImpl<ArtMethod>(ArtMethod* member,
-                                                      hiddenapi::ApiList api_list,
+                                                      ApiList api_list,
                                                       AccessMethod access_method);
 }  // namespace detail
 
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 1a5e010..c73a710 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -52,50 +52,91 @@
   kLinking,
 };
 
-struct AccessContext {
+// Represents the API domain of a caller/callee.
+class AccessContext {
  public:
-  explicit AccessContext(bool is_trusted) : is_trusted_(is_trusted) {}
+  // Initialize to either the fully-trusted or fully-untrusted domain.
+  explicit AccessContext(bool is_trusted)
+      : klass_(nullptr),
+        dex_file_(nullptr),
+        domain_(ComputeDomain(is_trusted)) {}
 
-  explicit AccessContext(ObjPtr<mirror::Class> klass) : is_trusted_(GetIsTrusted(klass)) {}
-
+  // Initialize from class loader and dex file (via dex cache).
   AccessContext(ObjPtr<mirror::ClassLoader> class_loader, ObjPtr<mirror::DexCache> dex_cache)
-      : is_trusted_(GetIsTrusted(class_loader, dex_cache)) {}
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      : klass_(nullptr),
+        dex_file_(GetDexFileFromDexCache(dex_cache)),
+        domain_(ComputeDomain(class_loader, dex_file_)) {}
 
-  bool IsTrusted() const { return is_trusted_; }
+  // Initialize from Class.
+  explicit AccessContext(ObjPtr<mirror::Class> klass)
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      : klass_(klass),
+        dex_file_(GetDexFileFromDexCache(klass->GetDexCache())),
+        domain_(ComputeDomain(klass, dex_file_)) {}
+
+  ObjPtr<mirror::Class> GetClass() const { return klass_; }
+  const DexFile* GetDexFile() const { return dex_file_; }
+  Domain GetDomain() const { return domain_; }
+
+  bool IsUntrustedDomain() const { return domain_ == Domain::kApplication; }
+
+  // Returns true if this domain is always allowed to access the domain of `callee`.
+  bool CanAlwaysAccess(const AccessContext& callee) const {
+    return IsDomainMoreTrustedThan(domain_, callee.domain_);
+  }
 
  private:
-  static bool GetIsTrusted(ObjPtr<mirror::ClassLoader> class_loader,
-                           ObjPtr<mirror::DexCache> dex_cache)
+  static const DexFile* GetDexFileFromDexCache(ObjPtr<mirror::DexCache> dex_cache)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    // Trust if the caller is in is boot class loader.
-    if (class_loader.IsNull()) {
-      return true;
-    }
-
-    // Trust if caller is in a platform dex file.
-    if (!dex_cache.IsNull()) {
-      const DexFile* dex_file = dex_cache->GetDexFile();
-      if (dex_file != nullptr && dex_file->IsPlatformDexFile()) {
-        return true;
-      }
-    }
-
-    return false;
+    return dex_cache.IsNull() ? nullptr : dex_cache->GetDexFile();
   }
 
-  static bool GetIsTrusted(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
-    DCHECK(!klass.IsNull());
+  static Domain ComputeDomain(bool is_trusted) {
+    return is_trusted ? Domain::kCorePlatform : Domain::kApplication;
+  }
 
-    if (klass->ShouldSkipHiddenApiChecks() && Runtime::Current()->IsJavaDebuggable()) {
-      // Class is known, it is marked trusted and we are in debuggable mode.
-      return true;
+  static Domain ComputeDomain(ObjPtr<mirror::ClassLoader> class_loader, const DexFile* dex_file)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (dex_file == nullptr) {
+      return ComputeDomain(/* is_trusted= */ class_loader.IsNull());
     }
 
+    Domain dex_domain = dex_file->GetHiddenapiDomain();
+    if (class_loader.IsNull() && dex_domain == Domain::kApplication) {
+      LOG(WARNING) << "DexFile " << dex_file->GetLocation() << " is in boot classpath "
+                   << "but is assigned untrusted domain";
+      dex_domain = Domain::kPlatform;
+    }
+    return dex_domain;
+  }
+
+  static Domain ComputeDomain(ObjPtr<mirror::Class> klass, const DexFile* dex_file)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
     // Check other aspects of the context.
-    return GetIsTrusted(klass->GetClassLoader(), klass->GetDexCache());
+    Domain domain = ComputeDomain(klass->GetClassLoader(), dex_file);
+
+    if (domain == Domain::kApplication &&
+        klass->ShouldSkipHiddenApiChecks() &&
+        Runtime::Current()->IsJavaDebuggable()) {
+      // Class is known, it is marked trusted and we are in debuggable mode.
+      domain = ComputeDomain(/* is_trusted= */ true);
+    }
+
+    return domain;
   }
 
-  bool is_trusted_;
+  // Pointer to declaring class of the caller/callee (null if not provided).
+  // This is not safe across GC but we're only using this class for passing
+  // information about the caller to the access check logic and never retain
+  // the AccessContext instance beyond that.
+  const ObjPtr<mirror::Class> klass_;
+
+  // DexFile of the caller/callee (null if not provided).
+  const DexFile* const dex_file_;
+
+  // Computed domain of the caller/callee.
+  const Domain domain_;
 };
 
 class ScopedHiddenApiEnforcementPolicySetting {
@@ -169,6 +210,12 @@
 uint32_t GetDexFlags(T* member) REQUIRES_SHARED(Locks::mutator_lock_);
 
 template<typename T>
+void MaybeReportCorePlatformApiViolation(T* member,
+                                         const AccessContext& caller_context,
+                                         AccessMethod access_method)
+    REQUIRES_SHARED(Locks::mutator_lock_);
+
+template<typename T>
 bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -178,12 +225,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)
@@ -304,44 +356,86 @@
                                      AccessMethod access_method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(member != nullptr);
+  const uint32_t runtime_flags = GetRuntimeFlags(member);
 
   // Exit early if member is public API. This flag is also set for non-boot class
   // path fields/methods.
-  if ((GetRuntimeFlags(member) & kAccPublicApi) != 0) {
+  if ((runtime_flags & kAccPublicApi) != 0) {
     return false;
   }
 
-  // Exit early if access checks are completely disabled.
-  if (Runtime::Current()->GetHiddenApiEnforcementPolicy() == EnforcementPolicy::kDisabled) {
+  // Determine which domain the caller and callee belong to.
+  // This can be *very* expensive. This is why ShouldDenyAccessToMember
+  // should not be called on every individual access.
+  const AccessContext caller_context = fn_get_access_context();
+  const AccessContext callee_context(member->GetDeclaringClass());
+
+  // Non-boot classpath callers should have exited early.
+  DCHECK(!callee_context.IsUntrustedDomain());
+
+  // Check if the caller is always allowed to access members in the callee context.
+  if (caller_context.CanAlwaysAccess(callee_context)) {
     return false;
   }
 
-  // Check if caller is exempted from access checks.
-  // This can be *very* expensive. Save it for last.
-  if (fn_get_access_context().IsTrusted()) {
-    return false;
+  // Check if this is platform accessing core platform. We may warn if `member` is
+  // not part of core platform API.
+  switch (caller_context.GetDomain()) {
+    case Domain::kApplication: {
+      DCHECK(!callee_context.IsUntrustedDomain());
+
+      // Exit early if access checks are completely disabled.
+      EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
+      if (policy == EnforcementPolicy::kDisabled) {
+        return false;
+      }
+
+      // 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(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);
+    }
+
+    case Domain::kPlatform: {
+      DCHECK(callee_context.GetDomain() == Domain::kCorePlatform);
+
+      // Member is part of core platform API. Accessing it is allowed.
+      if ((runtime_flags & kAccCorePlatformApi) != 0) {
+        return false;
+      }
+
+      // Allow access if access checks are disabled.
+      EnforcementPolicy policy = Runtime::Current()->GetCorePlatformApiEnforcementPolicy();
+      if (policy == EnforcementPolicy::kDisabled) {
+        return false;
+      }
+
+      // Access checks are not disabled, report the violation.
+      detail::MaybeReportCorePlatformApiViolation(member, caller_context, access_method);
+
+      // Deny access if the policy is enabled.
+      return policy == EnforcementPolicy::kEnabled;
+    }
+
+    case Domain::kCorePlatform: {
+      LOG(FATAL) << "CorePlatform domain should be allowed to access all domains";
+      UNREACHABLE();
+    }
   }
-
-  // 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));
-
-  // Member is hidden and caller is not exempted. Enter slow path.
-  return detail::ShouldDenyAccessToMemberImpl(member, api_list, access_method);
 }
 
 // Helper method for callers where access context can be determined beforehand.
 // Wraps AccessContext in a lambda and passes it to the real ShouldDenyAccessToMember.
 template<typename T>
 inline bool ShouldDenyAccessToMember(T* member,
-                                     AccessContext access_context,
+                                     const AccessContext& access_context,
                                      AccessMethod access_method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  return ShouldDenyAccessToMember(
-      member,
-      [&] () REQUIRES_SHARED(Locks::mutator_lock_) { return access_context; },
-      access_method);
+  return ShouldDenyAccessToMember(member, [&]() { return access_context; }, access_method);
 }
 
 }  // namespace hiddenapi
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 4fa7271..72a8727 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -185,13 +185,13 @@
 static ALWAYS_INLINE bool ShouldDenyAccessToMember(T* member, ShadowFrame* frame)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   // All uses in this file are from reflection
-  constexpr hiddenapi::AccessMethod access_method = hiddenapi::AccessMethod::kReflection;
+  constexpr hiddenapi::AccessMethod kAccessMethod = hiddenapi::AccessMethod::kReflection;
   return hiddenapi::ShouldDenyAccessToMember(
       member,
       [&]() REQUIRES_SHARED(Locks::mutator_lock_) {
         return hiddenapi::AccessContext(frame->GetMethod()->GetDeclaringClass());
       },
-      access_method);
+      kAccessMethod);
 }
 
 void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self,
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 52482b7..daf02fe 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -836,8 +836,9 @@
     return;
   }
 
+  // Assign core platform domain as the dex files are allowed to access all the other domains.
   for (const DexFile* dex_file : dex_files) {
-    const_cast<DexFile*>(dex_file)->SetIsPlatformDexFile();
+    const_cast<DexFile*>(dex_file)->SetHiddenapiDomain(hiddenapi::Domain::kCorePlatform);
   }
 }
 
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index d022c3b..4115791 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -130,7 +130,7 @@
 // callers (hiddenapi_context).
 template<typename T>
 ALWAYS_INLINE static bool IsDiscoverable(bool public_only,
-                                         hiddenapi::AccessContext access_context,
+                                         const hiddenapi::AccessContext& access_context,
                                          T* member)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   if (public_only && ((member->GetAccessFlags() & kAccPublic) == 0)) {
@@ -508,8 +508,9 @@
 }
 
 static ALWAYS_INLINE inline bool MethodMatchesConstructor(
-    ArtMethod* m, bool public_only, hiddenapi::AccessContext hiddenapi_context)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
+    ArtMethod* m,
+    bool public_only,
+    const hiddenapi::AccessContext& hiddenapi_context) REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(m != nullptr);
   return m->IsConstructor() &&
          !m->IsStatic() &&
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index d537de5..4853187 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -273,6 +273,7 @@
       is_low_memory_mode_(false),
       safe_mode_(false),
       hidden_api_policy_(hiddenapi::EnforcementPolicy::kDisabled),
+      core_platform_api_policy_(hiddenapi::EnforcementPolicy::kJustWarn),
       dedupe_hidden_api_warnings_(true),
       hidden_api_access_event_log_rate_(0),
       dump_native_stack_on_sig_quit_(true),
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 1d1d0d3..ee2c514 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -539,6 +539,14 @@
     return hidden_api_policy_;
   }
 
+  void SetCorePlatformApiEnforcementPolicy(hiddenapi::EnforcementPolicy policy) {
+    core_platform_api_policy_ = policy;
+  }
+
+  hiddenapi::EnforcementPolicy GetCorePlatformApiEnforcementPolicy() const {
+    return core_platform_api_policy_;
+  }
+
   void SetHiddenApiExemptions(const std::vector<std::string>& exemptions) {
     hidden_api_exemptions_ = exemptions;
   }
@@ -1075,23 +1083,17 @@
   // Whether access checks on hidden API should be performed.
   hiddenapi::EnforcementPolicy hidden_api_policy_;
 
+  // Whether access checks on core platform API should be performed.
+  hiddenapi::EnforcementPolicy core_platform_api_policy_;
+
   // List of signature prefixes of methods that have been removed from the blacklist, and treated
   // as if whitelisted.
   std::vector<std::string> hidden_api_exemptions_;
 
-  // Whether the application has used an API which is not restricted but we
-  // should issue a warning about it.
-  bool pending_hidden_api_warning_;
-
   // Do not warn about the same hidden API access violation twice.
   // This is only used for testing.
   bool dedupe_hidden_api_warnings_;
 
-  // Hidden API can print warnings into the log and/or set a flag read by the
-  // framework to show a UI warning. If this flag is set, always set the flag
-  // when there is a warning. This is only used for testing.
-  bool always_set_hidden_api_warning_flag_;
-
   // How often to log hidden API access to the event log. An integer between 0
   // (never) and 0x10000 (always).
   uint32_t hidden_api_access_event_log_rate_;
diff --git a/test/674-hiddenapi/hiddenapi-flags.csv b/test/674-hiddenapi/hiddenapi-flags.csv
index d875bdf..42626f7 100644
--- a/test/674-hiddenapi/hiddenapi-flags.csv
+++ b/test/674-hiddenapi/hiddenapi-flags.csv
@@ -1,30 +1,41 @@
+LNullaryConstructorBlacklistAndCorePlatformApi;-><init>()V,blacklist,core-platform-api
 LNullaryConstructorBlacklist;-><init>()V,blacklist
 LNullaryConstructorDarkGreylist;-><init>()V,greylist-max-o
 LNullaryConstructorLightGreylist;-><init>()V,greylist
+LParentClass;->fieldPackageBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
 LParentClass;->fieldPackageBlacklist:I,blacklist
 LParentClass;->fieldPackageDarkGreylist:I,greylist-max-o
 LParentClass;->fieldPackageLightGreylist:I,greylist
+LParentClass;->fieldPackageStaticBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
 LParentClass;->fieldPackageStaticBlacklist:I,blacklist
 LParentClass;->fieldPackageStaticDarkGreylist:I,greylist-max-o
 LParentClass;->fieldPackageStaticLightGreylist:I,greylist
+LParentClass;->fieldPrivateBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
 LParentClass;->fieldPrivateBlacklist:I,blacklist
 LParentClass;->fieldPrivateDarkGreylist:I,greylist-max-o
 LParentClass;->fieldPrivateLightGreylist:I,greylist
+LParentClass;->fieldPrivateStaticBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
 LParentClass;->fieldPrivateStaticBlacklist:I,blacklist
 LParentClass;->fieldPrivateStaticDarkGreylist:I,greylist-max-o
 LParentClass;->fieldPrivateStaticLightGreylist:I,greylist
+LParentClass;->fieldProtectedBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
 LParentClass;->fieldProtectedBlacklist:I,blacklist
 LParentClass;->fieldProtectedDarkGreylist:I,greylist-max-o
 LParentClass;->fieldProtectedLightGreylist:I,greylist
+LParentClass;->fieldProtectedStaticBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
 LParentClass;->fieldProtectedStaticBlacklist:I,blacklist
 LParentClass;->fieldProtectedStaticDarkGreylist:I,greylist-max-o
 LParentClass;->fieldProtectedStaticLightGreylist:I,greylist
+LParentClass;->fieldPublicBlacklistAndCorePlatformApiB:I,blacklist,core-platform-api
+LParentClass;->fieldPublicBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
 LParentClass;->fieldPublicBlacklistB:I,blacklist
 LParentClass;->fieldPublicBlacklist:I,blacklist
 LParentClass;->fieldPublicDarkGreylistB:I,greylist-max-o
 LParentClass;->fieldPublicDarkGreylist:I,greylist-max-o
 LParentClass;->fieldPublicLightGreylistB:I,greylist
 LParentClass;->fieldPublicLightGreylist:I,greylist
+LParentClass;->fieldPublicStaticBlacklistAndCorePlatformApiB:I,blacklist,core-platform-api
+LParentClass;->fieldPublicStaticBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
 LParentClass;->fieldPublicStaticBlacklistB:I,blacklist
 LParentClass;->fieldPublicStaticBlacklist:I,blacklist
 LParentClass;->fieldPublicStaticDarkGreylistB:I,greylist-max-o
@@ -33,49 +44,65 @@
 LParentClass;->fieldPublicStaticLightGreylist:I,greylist
 LParentClass;-><init>(DB)V,greylist-max-o
 LParentClass;-><init>(DC)V,blacklist
+LParentClass;-><init>(DI)V,blacklist,core-platform-api
 LParentClass;-><init>(DZ)V,greylist
 LParentClass;-><init>(FB)V,greylist-max-o
 LParentClass;-><init>(FC)V,blacklist
+LParentClass;-><init>(FI)V,blacklist,core-platform-api
 LParentClass;-><init>(FZ)V,greylist
 LParentClass;-><init>(IB)V,greylist-max-o
 LParentClass;-><init>(IC)V,blacklist
+LParentClass;-><init>(II)V,blacklist,core-platform-api
 LParentClass;-><init>(IZ)V,greylist
 LParentClass;-><init>(JB)V,greylist-max-o
 LParentClass;-><init>(JC)V,blacklist
+LParentClass;-><init>(JI)V,blacklist,core-platform-api
 LParentClass;-><init>(JZ)V,greylist
+LParentClass;->methodPackageBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
 LParentClass;->methodPackageBlacklist()I,blacklist
 LParentClass;->methodPackageDarkGreylist()I,greylist-max-o
 LParentClass;->methodPackageLightGreylist()I,greylist
+LParentClass;->methodPackageStaticBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
 LParentClass;->methodPackageStaticBlacklist()I,blacklist
 LParentClass;->methodPackageStaticDarkGreylist()I,greylist-max-o
 LParentClass;->methodPackageStaticLightGreylist()I,greylist
+LParentClass;->methodPrivateBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
 LParentClass;->methodPrivateBlacklist()I,blacklist
 LParentClass;->methodPrivateDarkGreylist()I,greylist-max-o
 LParentClass;->methodPrivateLightGreylist()I,greylist
+LParentClass;->methodPrivateStaticBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
 LParentClass;->methodPrivateStaticBlacklist()I,blacklist
 LParentClass;->methodPrivateStaticDarkGreylist()I,greylist-max-o
 LParentClass;->methodPrivateStaticLightGreylist()I,greylist
+LParentClass;->methodProtectedBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
 LParentClass;->methodProtectedBlacklist()I,blacklist
 LParentClass;->methodProtectedDarkGreylist()I,greylist-max-o
 LParentClass;->methodProtectedLightGreylist()I,greylist
+LParentClass;->methodProtectedStaticBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
 LParentClass;->methodProtectedStaticBlacklist()I,blacklist
 LParentClass;->methodProtectedStaticDarkGreylist()I,greylist-max-o
 LParentClass;->methodProtectedStaticLightGreylist()I,greylist
+LParentClass;->methodPublicBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
 LParentClass;->methodPublicBlacklist()I,blacklist
 LParentClass;->methodPublicDarkGreylist()I,greylist-max-o
 LParentClass;->methodPublicLightGreylist()I,greylist
+LParentClass;->methodPublicStaticBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
 LParentClass;->methodPublicStaticBlacklist()I,blacklist
 LParentClass;->methodPublicStaticDarkGreylist()I,greylist-max-o
 LParentClass;->methodPublicStaticLightGreylist()I,greylist
+LParentInterface;->fieldPublicStaticBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
 LParentInterface;->fieldPublicStaticBlacklist:I,blacklist
 LParentInterface;->fieldPublicStaticDarkGreylist:I,greylist-max-o
 LParentInterface;->fieldPublicStaticLightGreylist:I,greylist
+LParentInterface;->methodPublicBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
 LParentInterface;->methodPublicBlacklist()I,blacklist
 LParentInterface;->methodPublicDarkGreylist()I,greylist-max-o
+LParentInterface;->methodPublicDefaultBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
 LParentInterface;->methodPublicDefaultBlacklist()I,blacklist
 LParentInterface;->methodPublicDefaultDarkGreylist()I,greylist-max-o
 LParentInterface;->methodPublicDefaultLightGreylist()I,greylist
 LParentInterface;->methodPublicLightGreylist()I,greylist
+LParentInterface;->methodPublicStaticBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
 LParentInterface;->methodPublicStaticBlacklist()I,blacklist
 LParentInterface;->methodPublicStaticDarkGreylist()I,greylist-max-o
 LParentInterface;->methodPublicStaticLightGreylist()I,greylist
diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc
index 2464263..8dfb402 100644
--- a/test/674-hiddenapi/hiddenapi.cc
+++ b/test/674-hiddenapi/hiddenapi.cc
@@ -27,40 +27,59 @@
 namespace art {
 namespace Test674HiddenApi {
 
+std::vector<std::vector<std::unique_ptr<const DexFile>>> opened_dex_files;
+
 extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) {
   Runtime* runtime = Runtime::Current();
   runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+  runtime->SetCorePlatformApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
   runtime->SetTargetSdkVersion(
       static_cast<uint32_t>(hiddenapi::ApiList::GreylistMaxO().GetMaxAllowedSdkVersion()));
   runtime->SetDedupeHiddenApiWarnings(false);
 }
 
-extern "C" JNIEXPORT void JNICALL Java_Main_appendToBootClassLoader(
-    JNIEnv* env, jclass, jstring jpath) {
+extern "C" JNIEXPORT void JNICALL Java_Main_setDexDomain(
+    JNIEnv*, jclass, jint int_index, jboolean is_core_platform) {
+  size_t index = static_cast<size_t>(int_index);
+  CHECK_LT(index, opened_dex_files.size());
+  for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files[index]) {
+    const_cast<DexFile*>(dex_file.get())->SetHiddenapiDomain(
+        (is_core_platform == JNI_FALSE) ? hiddenapi::Domain::kPlatform
+                                        : hiddenapi::Domain::kCorePlatform);
+  }
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_appendToBootClassLoader(
+    JNIEnv* env, jclass klass, jstring jpath, jboolean is_core_platform) {
   ScopedUtfChars utf(env, jpath);
   const char* path = utf.c_str();
-  if (path == nullptr) {
-    return;
-  }
+  CHECK(path != nullptr);
+
+  const size_t index = opened_dex_files.size();
+  const jint int_index = static_cast<jint>(index);
+  opened_dex_files.push_back(std::vector<std::unique_ptr<const DexFile>>());
 
   ArtDexFileLoader dex_loader;
   std::string error_msg;
-  std::vector<std::unique_ptr<const DexFile>> dex_files;
+
   if (!dex_loader.Open(path,
                        path,
                        /* verify */ false,
                        /* verify_checksum */ true,
                        &error_msg,
-                       &dex_files)) {
+                       &opened_dex_files[index])) {
     LOG(FATAL) << "Could not open " << path << " for boot classpath extension: " << error_msg;
     UNREACHABLE();
   }
 
+  Java_Main_setDexDomain(env, klass, int_index, is_core_platform);
+
   ScopedObjectAccess soa(Thread::Current());
-  for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
-    Runtime::Current()->GetClassLinker()->AppendToBootClassPath(
-        Thread::Current(), *dex_file.release());
+  for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files[index]) {
+    Runtime::Current()->GetClassLinker()->AppendToBootClassPath(Thread::Current(), *dex_file.get());
   }
+
+  return int_index;
 }
 
 static jobject NewInstance(JNIEnv* env, jclass klass) {
diff --git a/test/674-hiddenapi/run b/test/674-hiddenapi/run
new file mode 100755
index 0000000..2babeef
--- /dev/null
+++ b/test/674-hiddenapi/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Make verification soft fail so that we can re-verify boot classpath
+# methods at runtime.
+exec ${RUN} $@ --verify-soft-fail
\ No newline at end of file
diff --git a/test/674-hiddenapi/src-art/Main.java b/test/674-hiddenapi/src-art/Main.java
index 782748c..190f4ac 100644
--- a/test/674-hiddenapi/src-art/Main.java
+++ b/test/674-hiddenapi/src-art/Main.java
@@ -28,6 +28,13 @@
 import java.util.zip.ZipFile;
 
 public class Main {
+  // This needs to be kept in sync with DexDomain in ChildClass.
+  enum DexDomain {
+    CorePlatform,
+    Platform,
+    Application
+  }
+
   public static void main(String[] args) throws Exception {
     System.loadLibrary(args[0]);
     prepareNativeLibFileName(args[0]);
@@ -40,70 +47,88 @@
     // or test the wrong thing. We rely on not deduping hidden API warnings
     // here for the same reasons), meaning the code under test and production
     // code are running in different configurations. Each test should be run in
-    // a fresh process to ensure that they are working correcting and not
-    // accidentally interfering with eachother.
+    // a fresh process to ensure that they are working correctly and not
+    // accidentally interfering with each other.
+    // As a side effect, we also cannot test Platform->Platform and later
+    // Platform->CorePlatform as the former succeeds in verifying linkage usages
+    // that should fail in the latter.
 
     // Run test with both parent and child dex files loaded with class loaders.
     // The expectation is that hidden members in parent should be visible to
     // the child.
-    doTest(false, false, false);
+    doTest(DexDomain.Application, DexDomain.Application, false);
     doUnloading();
 
     // Now append parent dex file to boot class path and run again. This time
     // the child dex file should not be able to access private APIs of the
     // parent.
-    appendToBootClassLoader(DEX_PARENT_BOOT);
-    doTest(true, false, false);
+    int parentIdx = appendToBootClassLoader(DEX_PARENT_BOOT, /* isCorePlatform */ false);
+    doTest(DexDomain.Platform, DexDomain.Application, false);
     doUnloading();
 
     // Now run the same test again, but with the blacklist exmemptions list set
     // to "L" which matches everything.
-    doTest(true, false, true);
+    doTest(DexDomain.Platform, DexDomain.Application, true);
     doUnloading();
 
-    // And finally append to child to boot class path as well. With both in the
-    // boot class path, access should be granted.
-    appendToBootClassLoader(DEX_CHILD);
-    doTest(true, true, false);
+    // Repeat the two tests above, only with parent being a core-platform dex file.
+    setDexDomain(parentIdx, /* isCorePlatform */ true);
+    doTest(DexDomain.CorePlatform, DexDomain.Application, false);
+    doUnloading();
+    doTest(DexDomain.CorePlatform, DexDomain.Application, true);
+    doUnloading();
+
+    // Append child to boot class path, first as a platform dex file.
+    // It should not be allowed to access non-public, non-core platform API members.
+    int childIdx = appendToBootClassLoader(DEX_CHILD, /* isCorePlatform */ false);
+    doTest(DexDomain.CorePlatform, DexDomain.Platform, false);
+    doUnloading();
+
+    // And finally change child to core-platform dex. With both in the boot classpath
+    // and both core-platform, access should be granted.
+    setDexDomain(childIdx, /* isCorePlatform */ true);
+    doTest(DexDomain.CorePlatform, DexDomain.CorePlatform, false);
     doUnloading();
   }
 
-  private static void doTest(boolean parentInBoot, boolean childInBoot, boolean whitelistAllApis)
-      throws Exception {
+  private static void doTest(DexDomain parentDomain, DexDomain childDomain,
+      boolean whitelistAllApis) throws Exception {
     // Load parent dex if it is not in boot class path.
     ClassLoader parentLoader = null;
-    if (parentInBoot) {
-      parentLoader = BOOT_CLASS_LOADER;
-    } else {
+    if (parentDomain == DexDomain.Application) {
       parentLoader = new PathClassLoader(DEX_PARENT, ClassLoader.getSystemClassLoader());
+    } else {
+      parentLoader = BOOT_CLASS_LOADER;
     }
 
     // Load child dex if it is not in boot class path.
     ClassLoader childLoader = null;
-    if (childInBoot) {
+    if (childDomain == DexDomain.Application) {
+      childLoader = new InMemoryDexClassLoader(readDexFile(DEX_CHILD), parentLoader);
+    } else {
       if (parentLoader != BOOT_CLASS_LOADER) {
         throw new IllegalStateException(
             "DeclaringClass must be in parent class loader of CallingClass");
       }
       childLoader = BOOT_CLASS_LOADER;
-    } else {
-      childLoader = new InMemoryDexClassLoader(readDexFile(DEX_CHILD), parentLoader);
     }
 
     // Create a unique copy of the native library. Each shared library can only
     // be loaded once, but for some reason even classes from a class loader
     // cannot register their native methods against symbols in a shared library
     // loaded by their parent class loader.
-    String nativeLibCopy = createNativeLibCopy(parentInBoot, childInBoot, whitelistAllApis);
+    String nativeLibCopy = createNativeLibCopy(parentDomain, childDomain, whitelistAllApis);
 
     if (whitelistAllApis) {
       VMRuntime.getRuntime().setHiddenApiExemptions(new String[]{"L"});
     }
 
     // Invoke ChildClass.runTest
-    Class.forName("ChildClass", true, childLoader)
-        .getDeclaredMethod("runTest", String.class, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE)
-            .invoke(null, nativeLibCopy, parentInBoot, childInBoot, whitelistAllApis);
+    Class<?> childClass = Class.forName("ChildClass", true, childLoader);
+    Method runTestMethod = childClass.getDeclaredMethod(
+        "runTest", String.class, Integer.TYPE, Integer.TYPE, Boolean.TYPE);
+    runTestMethod.invoke(null, nativeLibCopy, parentDomain.ordinal(), childDomain.ordinal(),
+        whitelistAllApis);
 
     VMRuntime.getRuntime().setHiddenApiExemptions(new String[0]);
   }
@@ -146,11 +171,11 @@
 
   // Copy native library to a new file with a unique name so it does not
   // conflict with other loaded instance of the same binary file.
-  private static String createNativeLibCopy(
-      boolean parentInBoot, boolean childInBoot, boolean whitelistAllApis) throws Exception {
+  private static String createNativeLibCopy(DexDomain parentDomain, DexDomain childDomain,
+      boolean whitelistAllApis) throws Exception {
     String tempFileName = System.mapLibraryName(
-        "hiddenapitest_" + (parentInBoot ? "1" : "0") + (childInBoot ? "1" : "0") +
-         (whitelistAllApis ? "1" : "0"));
+        "hiddenapitest_" + (parentDomain.ordinal()) + (childDomain.ordinal()) +
+        (whitelistAllApis ? "1" : "0"));
     File tempFile = new File(System.getenv("DEX_LOCATION"), tempFileName);
     Files.copy(new File(nativeLibFileName).toPath(), tempFile.toPath());
     return tempFile.getAbsolutePath();
@@ -175,6 +200,7 @@
 
   private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader();
 
-  private static native void appendToBootClassLoader(String dexPath);
+  private static native int appendToBootClassLoader(String dexPath, boolean isCorePlatform);
+  private static native void setDexDomain(int index, boolean isCorePlatform);
   private static native void init();
 }
diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java
index 3427b8e..f120bda 100644
--- a/test/674-hiddenapi/src-ex/ChildClass.java
+++ b/test/674-hiddenapi/src-ex/ChildClass.java
@@ -45,7 +45,8 @@
     Whitelist(PrimitiveType.TShort),
     LightGreylist(PrimitiveType.TBoolean),
     DarkGreylist(PrimitiveType.TByte),
-    Blacklist(PrimitiveType.TCharacter);
+    Blacklist(PrimitiveType.TCharacter),
+    BlacklistAndCorePlatformApi(PrimitiveType.TInteger);
 
     Hiddenness(PrimitiveType type) { mAssociatedType = type; }
     public PrimitiveType mAssociatedType;
@@ -67,19 +68,34 @@
     Denied,
   }
 
+  // This needs to be kept in sync with DexDomain in Main.
+  enum DexDomain {
+    CorePlatform,
+    Platform,
+    Application
+  }
+
   private static final boolean booleanValues[] = new boolean[] { false, true };
 
-  public static void runTest(String libFileName, boolean expectedParentInBoot,
-      boolean expectedChildInBoot, boolean everythingWhitelisted) throws Exception {
+  public static void runTest(String libFileName, int parentDomainOrdinal,
+      int childDomainOrdinal, boolean everythingWhitelisted) throws Exception {
     System.load(libFileName);
 
+    parentDomain = DexDomain.values()[parentDomainOrdinal];
+    childDomain = DexDomain.values()[childDomainOrdinal];
+
+    configMessage = "parentDomain=" + parentDomain.name() + ", childDomain=" + childDomain.name()
+        + ", everythingWhitelisted=" + everythingWhitelisted;
+
     // Check expectations about loading into boot class path.
-    isParentInBoot = (ParentClass.class.getClassLoader().getParent() == null);
+    boolean isParentInBoot = (ParentClass.class.getClassLoader().getParent() == null);
+    boolean expectedParentInBoot = (parentDomain != DexDomain.Application);
     if (isParentInBoot != expectedParentInBoot) {
       throw new RuntimeException("Expected ParentClass " +
                                  (expectedParentInBoot ? "" : "not ") + "in boot class path");
     }
-    isChildInBoot = (ChildClass.class.getClassLoader().getParent() == null);
+    boolean isChildInBoot = (ChildClass.class.getClassLoader().getParent() == null);
+    boolean expectedChildInBoot = (childDomain != DexDomain.Application);
     if (isChildInBoot != expectedChildInBoot) {
       throw new RuntimeException("Expected ChildClass " + (expectedChildInBoot ? "" : "not ") +
                                  "in boot class path");
@@ -92,14 +108,26 @@
     // Run meaningful combinations of access flags.
     for (Hiddenness hiddenness : Hiddenness.values()) {
       final Behaviour expected;
+      final boolean invokesMemberCallback;
       // Warnings are now disabled whenever access is granted, even for
       // greylisted APIs. This is the behaviour for release builds.
-      if (isSameBoot || everythingWhitelisted || hiddenness == Hiddenness.Whitelist) {
+      if (everythingWhitelisted || hiddenness == Hiddenness.Whitelist) {
         expected = Behaviour.Granted;
-      } else if (hiddenness == Hiddenness.Blacklist) {
+        invokesMemberCallback = false;
+      } else if (parentDomain == DexDomain.CorePlatform && childDomain == DexDomain.Platform) {
+        expected = (hiddenness == Hiddenness.BlacklistAndCorePlatformApi)
+            ? Behaviour.Granted : Behaviour.Denied;
+        invokesMemberCallback = false;
+      } else if (isSameBoot) {
+        expected = Behaviour.Granted;
+        invokesMemberCallback = false;
+      } else if (hiddenness == Hiddenness.Blacklist ||
+                 hiddenness == Hiddenness.BlacklistAndCorePlatformApi) {
         expected = Behaviour.Denied;
+        invokesMemberCallback = true;
       } else {
         expected = Behaviour.Warning;
+        invokesMemberCallback = true;
       }
 
       for (boolean isStatic : booleanValues) {
@@ -109,8 +137,10 @@
           // Test reflection and JNI on methods and fields
           for (Class klass : new Class<?>[] { ParentClass.class, ParentInterface.class }) {
             String baseName = visibility.name() + suffix;
-            checkField(klass, "field" + baseName, isStatic, visibility, expected);
-            checkMethod(klass, "method" + baseName, isStatic, visibility, expected);
+            checkField(klass, "field" + baseName, isStatic, visibility, expected,
+                invokesMemberCallback);
+            checkMethod(klass, "method" + baseName, isStatic, visibility, expected,
+                invokesMemberCallback);
           }
 
           // Check whether one can use a class constructor.
@@ -118,7 +148,8 @@
 
           // Check whether one can use an interface default method.
           String name = "method" + visibility.name() + "Default" + hiddenness.name();
-          checkMethod(ParentInterface.class, name, /*isStatic*/ false, visibility, expected);
+          checkMethod(ParentInterface.class, name, /*isStatic*/ false, visibility, expected,
+              invokesMemberCallback);
         }
 
         // Test whether static linking succeeds.
@@ -181,11 +212,10 @@
   }
 
   private static void checkField(Class<?> klass, String name, boolean isStatic,
-      Visibility visibility, Behaviour behaviour) throws Exception {
+      Visibility visibility, Behaviour behaviour, boolean invokesMemberCallback) throws Exception {
 
     boolean isPublic = (visibility == Visibility.Public);
     boolean canDiscover = (behaviour != Behaviour.Denied);
-    boolean invokesMemberCallback = (behaviour != Behaviour.Granted);
 
     if (klass.isInterface() && (!isStatic || !isPublic)) {
       // Interfaces only have public static fields.
@@ -275,7 +305,7 @@
   }
 
   private static void checkMethod(Class<?> klass, String name, boolean isStatic,
-      Visibility visibility, Behaviour behaviour) throws Exception {
+      Visibility visibility, Behaviour behaviour, boolean invokesMemberCallback) throws Exception {
 
     boolean isPublic = (visibility == Visibility.Public);
     if (klass.isInterface() && !isPublic) {
@@ -284,7 +314,6 @@
     }
 
     boolean canDiscover = (behaviour != Behaviour.Denied);
-    boolean invokesMemberCallback = (behaviour != Behaviour.Granted);
 
     // Test discovery with reflection.
 
@@ -428,8 +457,7 @@
 
     if (Reflection.canUseNewInstance(klass) != canAccess) {
       throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
-          "be able to construct " + klass.getName() + ". " +
-          "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+          "be able to construct " + klass.getName() + ". " + configMessage);
     }
   }
 
@@ -439,8 +467,7 @@
 
     if (Linking.canAccess(className, takesParameter) != canAccess) {
       throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
-          "be able to verify " + className + "." +
-          "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+          "be able to verify " + className + "." + configMessage);
     }
   }
 
@@ -448,16 +475,13 @@
       String fn, boolean canAccess) {
     throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() +
         "." + name + " to " + (canAccess ? "" : "not ") + "be discoverable with " + fn + ". " +
-        "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " +
-        "everythingWhitelisted = " + everythingWhitelisted);
+        configMessage);
   }
 
   private static void throwAccessException(Class<?> klass, String name, boolean isField,
       String fn) {
     throw new RuntimeException("Expected to be able to access " + (isField ? "field " : "method ") +
-        klass.getName() + "." + name + " using " + fn + ". " +
-        "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " +
-        "everythingWhitelisted = " + everythingWhitelisted);
+        klass.getName() + "." + name + " using " + fn + ". " + configMessage);
   }
 
   private static void throwModifiersException(Class<?> klass, String name, boolean isField) {
@@ -465,7 +489,9 @@
         "." + name + " to not expose hidden modifiers");
   }
 
-  private static boolean isParentInBoot;
-  private static boolean isChildInBoot;
+  private static DexDomain parentDomain;
+  private static DexDomain childDomain;
   private static boolean everythingWhitelisted;
+
+  private static String configMessage;
 }
diff --git a/test/674-hiddenapi/src-ex/Linking.java b/test/674-hiddenapi/src-ex/Linking.java
index 0fa0b19..5aa3663 100644
--- a/test/674-hiddenapi/src-ex/Linking.java
+++ b/test/674-hiddenapi/src-ex/Linking.java
@@ -62,6 +62,12 @@
   }
 }
 
+class LinkFieldGetBlacklistAndCorePlatformApi {
+  public static int access() {
+    return new ParentClass().fieldPublicBlacklistAndCorePlatformApi;
+  }
+}
+
 // INSTANCE FIELD SET
 
 class LinkFieldSetWhitelist {
@@ -92,6 +98,13 @@
   }
 }
 
+class LinkFieldSetBlacklistAndCorePlatformApi {
+  public static void access(int x) {
+    // Need to use a different field from the getter to bypass DexCache.
+    new ParentClass().fieldPublicBlacklistAndCorePlatformApiB = x;
+  }
+}
+
 // STATIC FIELD GET
 
 class LinkFieldGetStaticWhitelist {
@@ -118,6 +131,12 @@
   }
 }
 
+class LinkFieldGetStaticBlacklistAndCorePlatformApi {
+  public static int access() {
+    return ParentClass.fieldPublicStaticBlacklistAndCorePlatformApi;
+  }
+}
+
 // STATIC FIELD SET
 
 class LinkFieldSetStaticWhitelist {
@@ -148,6 +167,13 @@
   }
 }
 
+class LinkFieldSetStaticBlacklistAndCorePlatformApi {
+  public static void access(int x) {
+    // Need to use a different field from the getter to bypass DexCache.
+    ParentClass.fieldPublicStaticBlacklistAndCorePlatformApiB = x;
+  }
+}
+
 // INVOKE INSTANCE METHOD
 
 class LinkMethodWhitelist {
@@ -174,6 +200,12 @@
   }
 }
 
+class LinkMethodBlacklistAndCorePlatformApi {
+  public static int access() {
+    return new ParentClass().methodPublicBlacklistAndCorePlatformApi();
+  }
+}
+
 // INVOKE INSTANCE INTERFACE METHOD
 
 class LinkMethodInterfaceWhitelist {
@@ -200,6 +232,12 @@
   }
 }
 
+class LinkMethodInterfaceBlacklistAndCorePlatformApi {
+  public static int access() {
+    return DummyClass.getInterfaceInstance().methodPublicBlacklistAndCorePlatformApi();
+  }
+}
+
 // INVOKE STATIC METHOD
 
 class LinkMethodStaticWhitelist {
@@ -226,6 +264,12 @@
   }
 }
 
+class LinkMethodStaticBlacklistAndCorePlatformApi {
+  public static int access() {
+    return ParentClass.methodPublicStaticBlacklistAndCorePlatformApi();
+  }
+}
+
 // INVOKE INTERFACE STATIC METHOD
 
 class LinkMethodInterfaceStaticWhitelist {
@@ -251,3 +295,9 @@
     return ParentInterface.methodPublicStaticBlacklist();
   }
 }
+
+class LinkMethodInterfaceStaticBlacklistAndCorePlatformApi {
+  public static int access() {
+    return ParentInterface.methodPublicStaticBlacklistAndCorePlatformApi();
+  }
+}
diff --git a/test/674-hiddenapi/src/DummyClass.java b/test/674-hiddenapi/src/DummyClass.java
index 51281a2..afba747 100644
--- a/test/674-hiddenapi/src/DummyClass.java
+++ b/test/674-hiddenapi/src/DummyClass.java
@@ -19,6 +19,7 @@
   public int methodPublicLightGreylist() { return 2; }
   public int methodPublicDarkGreylist() { return 3; }
   public int methodPublicBlacklist() { return 4; }
+  public int methodPublicBlacklistAndCorePlatformApi() { return 5; }
 
   public static ParentInterface getInterfaceInstance() {
     return new DummyClass();
diff --git a/test/674-hiddenapi/src/NullaryConstructorBlacklistAndCorePlatformApi.java b/test/674-hiddenapi/src/NullaryConstructorBlacklistAndCorePlatformApi.java
new file mode 100644
index 0000000..86af29e
--- /dev/null
+++ b/test/674-hiddenapi/src/NullaryConstructorBlacklistAndCorePlatformApi.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class NullaryConstructorBlacklistAndCorePlatformApi {
+  public NullaryConstructorBlacklistAndCorePlatformApi() { x = 22; }
+  public NullaryConstructorBlacklistAndCorePlatformApi(int y) { x = y; }
+  protected int x;
+}
diff --git a/test/674-hiddenapi/src/ParentClass.java b/test/674-hiddenapi/src/ParentClass.java
index 07e84cc..1442392 100644
--- a/test/674-hiddenapi/src/ParentClass.java
+++ b/test/674-hiddenapi/src/ParentClass.java
@@ -43,6 +43,12 @@
   private int fieldPrivateBlacklist = 244;
   public int fieldPublicBlacklistB = 245;
 
+  public int fieldPublicBlacklistAndCorePlatformApi = 251;
+  int fieldPackageBlacklistAndCorePlatformApi = 252;
+  protected int fieldProtectedBlacklistAndCorePlatformApi = 253;
+  private int fieldPrivateBlacklistAndCorePlatformApi = 254;
+  public int fieldPublicBlacklistAndCorePlatformApiB = 255;
+
   // STATIC FIELD
 
   public static int fieldPublicStaticWhitelist = 111;
@@ -69,6 +75,12 @@
   private static int fieldPrivateStaticBlacklist = 144;
   public static int fieldPublicStaticBlacklistB = 145;
 
+  public static int fieldPublicStaticBlacklistAndCorePlatformApi = 151;
+  static int fieldPackageStaticBlacklistAndCorePlatformApi = 152;
+  protected static int fieldProtectedStaticBlacklistAndCorePlatformApi = 153;
+  private static int fieldPrivateStaticBlacklistAndCorePlatformApi = 154;
+  public static int fieldPublicStaticBlacklistAndCorePlatformApiB = 155;
+
   // INSTANCE METHOD
 
   public int methodPublicWhitelist() { return 411; }
@@ -91,6 +103,11 @@
   protected int methodProtectedBlacklist() { return 443; }
   private int methodPrivateBlacklist() { return 444; }
 
+  public int methodPublicBlacklistAndCorePlatformApi() { return 451; }
+  int methodPackageBlacklistAndCorePlatformApi() { return 452; }
+  protected int methodProtectedBlacklistAndCorePlatformApi() { return 453; }
+  private int methodPrivateBlacklistAndCorePlatformApi() { return 454; }
+
   // STATIC METHOD
 
   public static int methodPublicStaticWhitelist() { return 311; }
@@ -113,6 +130,11 @@
   protected static int methodProtectedStaticBlacklist() { return 343; }
   private static int methodPrivateStaticBlacklist() { return 344; }
 
+  public static int methodPublicStaticBlacklistAndCorePlatformApi() { return 351; }
+  static int methodPackageStaticBlacklistAndCorePlatformApi() { return 352; }
+  protected static int methodProtectedStaticBlacklistAndCorePlatformApi() { return 353; }
+  private static int methodPrivateStaticBlacklistAndCorePlatformApi() { return 354; }
+
   // CONSTRUCTOR
 
   // Whitelist
@@ -139,6 +161,12 @@
   protected ParentClass(long x, char y) {}
   private ParentClass(double x, char y) {}
 
+  // Blacklist and CorePlatformApi
+  public ParentClass(int x, int y) {}
+  ParentClass(float x, int y) {}
+  protected ParentClass(long x, int y) {}
+  private ParentClass(double x, int y) {}
+
   // HELPERS
 
   public int callMethodPublicWhitelist() { return methodPublicWhitelist(); }
@@ -157,4 +185,15 @@
   public int callMethodPackageBlacklist() { return methodPackageBlacklist(); }
   public int callMethodProtectedBlacklist() { return methodProtectedBlacklist(); }
 
+  public int callMethodPublicBlacklistAndCorePlatformApi() {
+    return methodPublicBlacklistAndCorePlatformApi();
+  }
+
+  public int callMethodPackageBlacklistAndCorePlatformApi() {
+    return methodPackageBlacklistAndCorePlatformApi();
+  }
+
+  public int callMethodProtectedBlacklistAndCorePlatformApi() {
+    return methodProtectedBlacklistAndCorePlatformApi();
+  }
 }
diff --git a/test/674-hiddenapi/src/ParentInterface.java b/test/674-hiddenapi/src/ParentInterface.java
index f79ac9d..1c5b58f 100644
--- a/test/674-hiddenapi/src/ParentInterface.java
+++ b/test/674-hiddenapi/src/ParentInterface.java
@@ -20,22 +20,26 @@
   static int fieldPublicStaticLightGreylist = 12;
   static int fieldPublicStaticDarkGreylist = 13;
   static int fieldPublicStaticBlacklist = 14;
+  static int fieldPublicStaticBlacklistAndCorePlatformApi = 15;
 
   // INSTANCE METHOD
   int methodPublicWhitelist();
   int methodPublicLightGreylist();
   int methodPublicDarkGreylist();
   int methodPublicBlacklist();
+  int methodPublicBlacklistAndCorePlatformApi();
 
   // STATIC METHOD
   static int methodPublicStaticWhitelist() { return 21; }
   static int methodPublicStaticLightGreylist() { return 22; }
   static int methodPublicStaticDarkGreylist() { return 23; }
   static int methodPublicStaticBlacklist() { return 24; }
+  static int methodPublicStaticBlacklistAndCorePlatformApi() { return 25; }
 
   // DEFAULT METHOD
   default int methodPublicDefaultWhitelist() { return 31; }
   default int methodPublicDefaultLightGreylist() { return 32; }
   default int methodPublicDefaultDarkGreylist() { return 33; }
   default int methodPublicDefaultBlacklist() { return 34; }
+  default int methodPublicDefaultBlacklistAndCorePlatformApi() { return 35; }
 }
diff --git a/test/999-redefine-hiddenapi/src/Main.java b/test/999-redefine-hiddenapi/src/Main.java
index 3d9bbda..014ea16 100644
--- a/test/999-redefine-hiddenapi/src/Main.java
+++ b/test/999-redefine-hiddenapi/src/Main.java
@@ -27,7 +27,7 @@
     init();
 
     // Load the '-ex' APK and attach it to the boot class path.
-    appendToBootClassLoader(DEX_EXTRA);
+    appendToBootClassLoader(DEX_EXTRA, /* isCorePlatform */ false);
 
     // Find the test class in boot class loader and verify that its members are hidden.
     Class<?> klass = Class.forName("art.Test999", true, BOOT_CLASS_LOADER);
@@ -67,7 +67,7 @@
   private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader();
 
   // Native functions. Note that these are implemented in 674-hiddenapi/hiddenapi.cc.
-  private static native void appendToBootClassLoader(String dexPath);
+  private static native void appendToBootClassLoader(String dexPath, boolean isCorePlatform);
   private static native void init();
 
   /**
diff --git a/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java b/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
index 1da848b..9c4e57e 100644
--- a/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
+++ b/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
@@ -17,6 +17,7 @@
 package com.android.class2greylist;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Splitter;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMap.Builder;
 import com.google.common.collect.ImmutableSet;
@@ -34,10 +35,12 @@
 import java.io.File;
 import java.io.IOException;
 import java.nio.charset.Charset;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * Build time tool for extracting a list of members from jar files that have the @UsedByApps
@@ -67,7 +70,6 @@
     }
 
     private final Status mStatus;
-    private final String mPublicApiListFile;
     private final String mCsvFlagsFile;
     private final String mCsvMetadataFile;
     private final String[] mJarFiles;
@@ -77,10 +79,11 @@
     public static void main(String[] args) {
         Options options = new Options();
         options.addOption(OptionBuilder
-                .withLongOpt("public-api-list")
+                .withLongOpt("stub-api-flags")
                 .hasArgs(1)
-                .withDescription("Public API list file. Used to de-dupe bridge methods.")
-                .create("p"));
+                .withDescription("CSV file with API flags generated from public API stubs. " +
+                        "Used to de-dupe bridge methods.")
+                .create("s"));
         options.addOption(OptionBuilder
                 .withLongOpt("write-flags-csv")
                 .hasArgs(1)
@@ -139,7 +142,7 @@
             try {
                 Class2Greylist c2gl = new Class2Greylist(
                         status,
-                        cmd.getOptionValue('p', null),
+                        cmd.getOptionValue('s', null),
                         cmd.getOptionValue('w', null),
                         cmd.getOptionValue('c', null),
                         jarFiles);
@@ -158,11 +161,10 @@
     }
 
     @VisibleForTesting
-    Class2Greylist(Status status, String publicApiListFile, String csvFlagsFile,
+    Class2Greylist(Status status, String stubApiFlagsFile, String csvFlagsFile,
             String csvMetadataFile, String[] jarFiles)
             throws IOException {
         mStatus = status;
-        mPublicApiListFile = publicApiListFile;
         mCsvFlagsFile = csvFlagsFile;
         mCsvMetadataFile = csvMetadataFile;
         mJarFiles = jarFiles;
@@ -172,9 +174,13 @@
             mOutput = new HiddenapiFlagsWriter(mCsvFlagsFile);
         }
 
-        if (mPublicApiListFile != null) {
-            mPublicApis = Sets.newHashSet(
-                    Files.readLines(new File(mPublicApiListFile), Charset.forName("UTF-8")));
+        if (stubApiFlagsFile != null) {
+            mPublicApis =
+                    Files.readLines(new File(stubApiFlagsFile), Charset.forName("UTF-8")).stream()
+                        .map(s -> Splitter.on(",").splitToList(s))
+                        .filter(s -> s.contains(FLAG_WHITELIST))
+                        .map(s -> s.get(0))
+                        .collect(Collectors.toSet());
         } else {
             mPublicApis = Collections.emptySet();
         }
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
index fdb0d8e..97fbcbf 100644
--- a/tools/hiddenapi/hiddenapi.cc
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -33,6 +33,7 @@
 #include "dex/dex_file-inl.h"
 
 namespace art {
+namespace hiddenapi {
 
 const char kErrorHelp[] = "\nSee go/hiddenapi-error for help.";
 
@@ -83,12 +84,12 @@
   UsageError("");
   UsageError("  Command \"list\": dump lists of public and private API");
   UsageError("    --boot-dex=<filename>: dex file which belongs to boot class path");
-  UsageError("    --stub-classpath=<filenames>: colon-separated list of dex/apk files");
-  UsageError("        which form API stubs of boot class path. Multiple classpaths can");
-  UsageError("        be specified");
+  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("");
-  UsageError("    --out-public=<filename>: output file for a list of all public APIs");
-  UsageError("    --out-private=<filename>: output file for a list of all private APIs");
+  UsageError("    --out-api-flags=<filename>: output file for a CSV file with API flags");
   UsageError("");
 
   exit(EXIT_FAILURE);
@@ -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(hiddenapi::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.
@@ -910,7 +911,7 @@
           } else if (option.starts_with("--output-dex=")) {
             output_dex_paths_.push_back(option.substr(strlen("--output-dex=")).ToString());
           } else if (option.starts_with("--api-flags=")) {
-            api_list_path_ = option.substr(strlen("--api-flags=")).ToString();
+            api_flags_path_ = option.substr(strlen("--api-flags=")).ToString();
           } else if (option == "--no-force-assign-all") {
             force_assign_all_ = false;
           } else {
@@ -923,13 +924,16 @@
           const StringPiece option(argv[i]);
           if (option.starts_with("--boot-dex=")) {
             boot_dex_paths_.push_back(option.substr(strlen("--boot-dex=")).ToString());
-          } else if (option.starts_with("--stub-classpath=")) {
-            stub_classpaths_.push_back(android::base::Split(
-                option.substr(strlen("--stub-classpath=")).ToString(), ":"));
-          } else if (option.starts_with("--out-public=")) {
-            out_public_path_ = option.substr(strlen("--out-public=")).ToString();
-          } else if (option.starts_with("--out-private=")) {
-            out_private_path_ = option.substr(strlen("--out-private=")).ToString();
+          } else if (option.starts_with("--public-stub-classpath=")) {
+            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 {
             Usage("Unknown argument '%s'", option.data());
           }
@@ -951,7 +955,7 @@
     }
 
     // Load dex signatures.
-    std::map<std::string, hiddenapi::ApiList> api_list = OpenApiFile(api_list_path_);
+    std::map<std::string, ApiList> api_list = OpenApiFile(api_flags_path_);
 
     // Iterate over input dex files and insert HiddenapiClassData sections.
     for (size_t i = 0; i < boot_dex_paths_.size(); ++i) {
@@ -975,7 +979,7 @@
               LOG(WARNING) << "Could not find hiddenapi flags for dex entry: "
                            << boot_member.GetApiEntry();
             }
-            builder.WriteFlags(api_list_found ? it->second : hiddenapi::ApiList::Whitelist());
+            builder.WriteFlags(api_list_found ? it->second : ApiList::Whitelist());
           };
           auto fn_field = [&](const ClassAccessor::Field& boot_field) {
             fn_shared(DexMember(boot_class, boot_field));
@@ -994,34 +998,29 @@
     }
   }
 
-  std::map<std::string, hiddenapi::ApiList> OpenApiFile(const std::string& path) {
+  std::map<std::string, ApiList> OpenApiFile(const std::string& path) {
     CHECK(!path.empty());
     std::ifstream api_file(path, std::ifstream::in);
     CHECK(!api_file.fail()) << "Unable to open file '" << path << "' " << strerror(errno);
 
-    std::map<std::string, hiddenapi::ApiList> api_flag_map;
+    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);
     }
@@ -1035,15 +1034,13 @@
       Usage("No boot DEX files specified");
     } else if (stub_classpaths_.empty()) {
       Usage("No stub DEX files specified");
-    } else if (out_public_path_.empty()) {
-      Usage("No public API output path specified");
-    } else if (out_private_path_.empty()) {
-      Usage("No private API output path specified");
+    } else if (api_flags_path_.empty()) {
+      Usage("No output path specified");
     }
 
     // Complete list of boot class path members. The associated boolean states
     // whether it is public (true) or private (false).
-    std::map<std::string, bool> boot_members;
+    std::map<std::string, ApiList> boot_members;
 
     // Deduplicate errors before printing them.
     std::set<std::string> unresolved;
@@ -1053,31 +1050,33 @@
     Hierarchy boot_hierarchy(boot_classpath);
 
     // Mark all boot dex members private.
-    boot_classpath.ForEachDexMember([&boot_members](const DexMember& boot_member) {
-      boot_members[boot_member.GetApiEntry()] = false;
+    boot_classpath.ForEachDexMember([&](const DexMember& boot_member) {
+      boot_members[boot_member.GetApiEntry()] = ApiList();
     });
 
     // Resolve each SDK dex member against the framework and mark it white.
-    for (const std::vector<std::string>& stub_classpath_dex : stub_classpaths_) {
-      ClassPath stub_classpath(stub_classpath_dex, /* open_writable= */ false);
+    for (const auto& cp_entry : stub_classpaths_) {
+      ClassPath stub_classpath(android::base::Split(cp_entry.first, ":"),
+                               /* open_writable= */ false);
       Hierarchy stub_hierarchy(stub_classpath);
+      const ApiList stub_api_list = cp_entry.second;
+
       stub_classpath.ForEachDexMember(
-          [&stub_hierarchy, &boot_hierarchy, &boot_members, &unresolved](
-              const DexMember& stub_member) {
+          [&](const DexMember& stub_member) {
             if (!stub_hierarchy.IsMemberVisible(stub_member)) {
               // Typically fake constructors and inner-class `this` fields.
               return;
             }
             bool resolved = boot_hierarchy.ForEachResolvableMember(
                 stub_member,
-                [&boot_members](const DexMember& boot_member) {
+                [&](const DexMember& boot_member) {
                   std::string entry = boot_member.GetApiEntry();
                   auto it = boot_members.find(entry);
                   CHECK(it != boot_members.end());
-                  if (it->second) {
+                  if (it->second.Contains(stub_api_list)) {
                     return false;  // has been marked before
                   } else {
-                    it->second = true;
+                    it->second |= stub_api_list;
                     return true;  // marked for the first time
                   }
                 });
@@ -1093,17 +1092,15 @@
     }
 
     // Write into public/private API files.
-    std::ofstream file_public(out_public_path_.c_str());
-    std::ofstream file_private(out_private_path_.c_str());
-    for (const std::pair<const std::string, bool>& entry : boot_members) {
-      if (entry.second) {
-        file_public << entry.first << std::endl;
+    std::ofstream file_flags(api_flags_path_.c_str());
+    for (const auto& entry : boot_members) {
+      if (entry.second.IsEmpty()) {
+        file_flags << entry.first << std::endl;
       } else {
-        file_private << entry.first << std::endl;
+        file_flags << entry.first << "," << entry.second << std::endl;
       }
     }
-    file_public.close();
-    file_private.close();
+    file_flags.close();
   }
 
   // Whether to check that all dex entries have been assigned flags.
@@ -1118,23 +1115,21 @@
 
   // Set of public API stub classpaths. Each classpath is formed by a list
   // of DEX/APK files in the order they appear on the classpath.
-  std::vector<std::vector<std::string>> stub_classpaths_;
+  std::vector<std::pair<std::string, ApiList>> stub_classpaths_;
 
-  // Paths to text files which contain the lists of API members.
-  std::string api_list_path_;
-
-  // Paths to text files to which we will output list of all API members.
-  std::string out_public_path_;
-  std::string out_private_path_;
+  // Path to CSV file containing the list of API members and their flags.
+  // This could be both an input and output path.
+  std::string api_flags_path_;
 };
 
+}  // namespace hiddenapi
 }  // namespace art
 
 int main(int argc, char** argv) {
-  art::original_argc = argc;
-  art::original_argv = argv;
+  art::hiddenapi::original_argc = argc;
+  art::hiddenapi::original_argv = argv;
   android::base::InitLogging(argv);
   art::MemMap::Init();
-  art::HiddenApi().Run(argc, argv);
+  art::hiddenapi::HiddenApi().Run(argc, argv);
   return EXIT_SUCCESS;
 }
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;
         }