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