diff options
author | 2018-12-14 14:36:15 +0000 | |
---|---|---|
committer | 2019-01-21 09:48:59 +0000 | |
commit | 90faceb71e25748172ba6369209f8a2a66735394 (patch) | |
tree | 64707bbd718b29de9a19c01b12b0cc872685bb13 | |
parent | 62a4bcf3fe11e6800f5d451b41003c135358ed6a (diff) |
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
-rw-r--r-- | dexlayout/dexlayout.cc | 4 | ||||
-rw-r--r-- | libartbase/base/hiddenapi_flags.cc | 3 | ||||
-rw-r--r-- | libartbase/base/hiddenapi_flags.h | 261 | ||||
-rw-r--r-- | libdexfile/dex/class_accessor-inl.h | 4 | ||||
-rw-r--r-- | libdexfile/dex/dex_file_verifier.cc | 2 | ||||
-rw-r--r-- | libdexfile/dex/modifiers.h | 4 | ||||
-rw-r--r-- | runtime/art_method.cc | 2 | ||||
-rw-r--r-- | runtime/hidden_api.cc | 38 | ||||
-rw-r--r-- | runtime/hidden_api.h | 16 | ||||
-rw-r--r-- | tools/hiddenapi/hiddenapi.cc | 55 | ||||
-rw-r--r-- | tools/hiddenapi/hiddenapi_test.cc | 4 | ||||
-rw-r--r-- | tools/veridex/hidden_api.cc | 9 | ||||
-rw-r--r-- | tools/veridex/hidden_api.h | 4 | ||||
-rw-r--r-- | tools/veridex/veridex.cc | 2 |
14 files changed, 286 insertions, 122 deletions
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index ef2c9e4d61..7382a97d35 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -224,7 +224,9 @@ static char* CreateAccessFlagStr(uint32_t flags, AccessFor for_what) { } 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 6caa75c570..ea57cb7c5e 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 b89882812a..c46872334a 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 @@ namespace hiddenapi { */ 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) & kValueBitMask), + kMin = kWhitelist, + kMax = kGreylistMaxP, + }; + + // 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 - kInvalid = static_cast<uint32_t>(-1), - kMinValue = kWhitelist, - kMaxValue = kGreylistMaxP, + kMin = kCorePlatformApi, + kMax = kCorePlatformApi, }; - static constexpr const char* kNames[] = { + // 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 @@ class ApiList { "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 @@ class ApiList { /* 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)); + // 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 (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(); } - // Returns the ApiList with a given name. - 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; + // 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; } - return Invalid(); + if (out_api_list != nullptr) { + *out_api_list = api_list; + } + return true; + } + + 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); } - bool operator==(const ApiList other) const { return value_ == other.value_; } - bool operator!=(const ApiList other) const { return !(*this == other); } + // 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(); + } + } - bool IsValid() const { return *this != Invalid(); } + const ApiList& operator|=(const ApiList& other) { + (*this) = (*this) | other; + return *this; + } - IntValueType GetIntValue() const { - DCHECK(IsValid()); - return static_cast<IntValueType>(value_); + // 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()); } - const char* GetName() const { return IsValid() ? kNames[GetIntValue()]: kInvalidName; } + // 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 8562d0583c..f224a29fc4 100644 --- a/libdexfile/dex/class_accessor-inl.h +++ b/libdexfile/dex/class_accessor-inl.h @@ -69,7 +69,7 @@ inline void ClassAccessor::Method::Read() { 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 @@ inline void ClassAccessor::Field::Read() { 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 f376c4dc7b..86a28e5a3e 100644 --- a/libdexfile/dex/dex_file_verifier.cc +++ b/libdexfile/dex/dex_file_verifier.cc @@ -1630,7 +1630,7 @@ bool DexFileVerifier::CheckIntraHiddenapiClassData() { 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 114c8e63e3..0c79c96765 100644 --- a/libdexfile/dex/modifiers.h +++ b/libdexfile/dex/modifiers.h @@ -85,7 +85,7 @@ static constexpr uint32_t kAccMustCountLocks = 0x04000000; // method (ru 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 @@ static constexpr uint32_t kAccHasDefaultMethod = 0x40000000; // 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 e273d94623..07193b2546 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -727,7 +727,7 @@ void ArtMethod::SetIntrinsic(uint32_t intrinsic) { // 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 2d3493d60e..adcf6b3226 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -267,9 +267,6 @@ static ALWAYS_INLINE void MaybeWhitelistMember(Runtime* runtime, T* member) } } -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 @@ uint32_t GetDexFlags(T* member) REQUIRES_SHARED(Locks::mutator_lock_) { 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 @@ uint32_t GetDexFlags(T* member) REQUIRES_SHARED(Locks::mutator_lock_) { // 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 @@ uint32_t GetDexFlags(T* member) REQUIRES_SHARED(Locks::mutator_lock_) { 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 1a5e010a7d..f08503369c 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -178,12 +178,17 @@ bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod acce 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 @@ inline bool ShouldDenyAccessToMember(T* member, // 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 49e528294c..97fbcbf3e0 100644 --- a/tools/hiddenapi/hiddenapi.cc +++ b/tools/hiddenapi/hiddenapi.cc @@ -85,6 +85,7 @@ NO_RETURN static void Usage(const char* fmt, ...) { 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 @@ class HiddenapiClassDataBuilder final { // 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 @@ class HiddenApi final { 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 @@ class HiddenApi final { 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; - const std::string& signature = values[0]; - - CHECK(api_flag_map.find(signature) == api_flag_map.end()) << "Duplicate entry in " << path - << ": " << signature << kErrorHelp; + CHECK_GT(values.size(), 1u) << path << ":" << line_number + << ": No flags found: " << line << 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& signature = values[0]; + CHECK(api_flag_map.find(signature) == api_flag_map.end()) << path << ":" << line_number + << ": Duplicate entry: " << signature << 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 @@ class HiddenApi final { // 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 @@ class HiddenApi final { 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 @@ class HiddenApi final { // 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 2689eedc10..7ef5b3d2b8 100644 --- a/tools/hiddenapi/hiddenapi_test.cc +++ b/tools/hiddenapi/hiddenapi_test.cc @@ -138,7 +138,7 @@ class HiddenApiTest : public CommonRuntimeTest { 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 @@ class HiddenApiTest : public CommonRuntimeTest { 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 2af7b50a73..1dae93a55b 100644 --- a/tools/veridex/hidden_api.cc +++ b/tools/veridex/hidden_api.cc @@ -30,13 +30,12 @@ HiddenApi::HiddenApi(const char* filename, bool sdk_uses_only) { 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 d6a6438fac..3c7f29ad23 100644 --- a/tools/veridex/hidden_api.h +++ b/tools/veridex/hidden_api.h @@ -37,11 +37,11 @@ class HiddenApi { 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 46ab8aa0e3..3b6c7f9f39 100644 --- a/tools/veridex/veridex.cc +++ b/tools/veridex/veridex.cc @@ -255,7 +255,7 @@ class Veridex { << 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; } |