diff options
-rw-r--r-- | dexdump/dexdump.cc | 2 | ||||
-rw-r--r-- | libartbase/base/hiddenapi_flags.h | 241 | ||||
-rw-r--r-- | libdexfile/dex/class_accessor-inl.h | 4 | ||||
-rw-r--r-- | libdexfile/dex/dex_file_verifier.cc | 2 | ||||
-rw-r--r-- | runtime/hidden_api.cc | 15 | ||||
-rw-r--r-- | runtime/hidden_api.h | 2 | ||||
-rw-r--r-- | runtime/hidden_api_test.cc | 51 | ||||
-rw-r--r-- | runtime/mirror/class.cc | 3 | ||||
-rw-r--r-- | tools/hiddenapi/hiddenapi.cc | 6 | ||||
-rw-r--r-- | tools/hiddenapi/hiddenapi_test.cc | 14 | ||||
-rw-r--r-- | tools/veridex/api_list_filter.h | 7 | ||||
-rw-r--r-- | tools/veridex/hidden_api.cc | 2 | ||||
-rw-r--r-- | tools/veridex/hidden_api.h | 2 | ||||
-rw-r--r-- | tools/veridex/veridex.cc | 4 |
14 files changed, 199 insertions, 156 deletions
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 43ed224b01..dd90d90cf2 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -1341,7 +1341,7 @@ static void dumpCode(const DexFile* pDexFile, u4 idx, u4 flags, static std::string GetHiddenapiFlagStr(uint32_t hiddenapi_flags) { std::stringstream ss; - hiddenapi::ApiList api_list(hiddenapi_flags); + hiddenapi::ApiList api_list = hiddenapi::ApiList::FromDexFlags(hiddenapi_flags); api_list.Dump(ss); std::string str_api_list = ss.str(); std::transform(str_api_list.begin(), str_api_list.end(), str_api_list.begin(), ::toupper); diff --git a/libartbase/base/hiddenapi_flags.h b/libartbase/base/hiddenapi_flags.h index 0d7938aca1..9ab8c759c3 100644 --- a/libartbase/base/hiddenapi_flags.h +++ b/libartbase/base/hiddenapi_flags.h @@ -17,15 +17,15 @@ #ifndef ART_LIBARTBASE_BASE_HIDDENAPI_FLAGS_H_ #define ART_LIBARTBASE_BASE_HIDDENAPI_FLAGS_H_ -#include "sdk_version.h" +#include <android-base/logging.h> #include <vector> -#include "android-base/logging.h" #include "base/bit_utils.h" #include "base/dumpable.h" -#include "base/macros.h" #include "base/hiddenapi_stubs.h" +#include "base/macros.h" +#include "sdk_version.h" namespace art { namespace hiddenapi { @@ -80,15 +80,20 @@ namespace helper { */ class ApiList { private: + // The representation in dex_flags_ is a combination of a Value in the lowest + // kValueBitSize bits, and bit flags corresponding to DomainApi in bits above + // that. + uint32_t dex_flags_; + // Number of bits reserved for Value in dex flags, and the corresponding bit mask. static constexpr uint32_t kValueBitSize = 4; static constexpr uint32_t kValueBitMask = helper::BitMask(kValueBitSize); enum class Value : uint32_t { // Values independent of target SDK version of app - kSdk = 0, - kUnsupported = 1, - kBlocked = 2, + kSdk = 0, + kUnsupported = 1, // @UnsupportedAppUsage + kBlocked = 2, // Values dependent on target SDK version of app. Put these last as // their list will be extended in future releases. @@ -100,21 +105,24 @@ class ApiList { kMaxTargetR = 6, kMaxTargetS = 7, - // Special values - kInvalid = (static_cast<uint32_t>(-1) & kValueBitMask), - kMin = kSdk, - kMax = kMaxTargetS, + // Invalid value. Does not imply the DomainApi is invalid. + kInvalid = (static_cast<uint32_t>(-1) & kValueBitMask), + + kMin = kSdk, + kMax = kMaxTargetS, + kFuture = kMax + 1, // Only for testing }; - // Additional bit flags after the first kValueBitSize bits in dex flags. - // These are used for domain-specific API. + // Additional bit flags after the first kValueBitSize bits in dex flags. These + // are used for domain-specific APIs. The app domain is the default when no + // bits are set. enum class DomainApi : uint32_t { kCorePlatformApi = kValueBitSize, kTestApi = kValueBitSize + 1, // Special values - kMin = kCorePlatformApi, - kMax = kTestApi, + kMin = kCorePlatformApi, + kMax = kTestApi, }; // Bit mask of all domain API flags. @@ -124,12 +132,22 @@ class ApiList { static_assert(kValueBitSize >= MinimumBitsToStore(helper::ToUint(Value::kMax)), "Not enough bits to store all ApiList values"); - // Checks that all Values are covered by kValueBitMask. + // Check that all Values are covered by kValueBitMask. static_assert(helper::MatchesBitMask(Value::kMin, kValueBitMask)); static_assert(helper::MatchesBitMask(Value::kMax, kValueBitMask)); + static_assert(helper::MatchesBitMask(Value::kFuture, kValueBitMask)); + static_assert(helper::MatchesBitMask(Value::kInvalid, kValueBitMask)); + + // Check that there's no offset between Values and the corresponding uint32 + // dex flags, so they can be converted between each other without any change. + static_assert(helper::ToUint(Value::kMin) == 0); - // Assert that Value::kInvalid is larger than the maximum Value. - static_assert(helper::ToUint(Value::kMax) < helper::ToUint(Value::kInvalid)); + // Check that Value::kInvalid is larger than kFuture (which is larger than kMax). + static_assert(helper::ToUint(Value::kFuture) < helper::ToUint(Value::kInvalid)); + + // Check that no DomainApi bit flag is covered by kValueBitMask. + static_assert((helper::ToBit(DomainApi::kMin) & kValueBitMask) == 0); + static_assert((helper::ToBit(DomainApi::kMax) & kValueBitMask) == 0); // Names corresponding to Values. static constexpr const char* kValueNames[] = { @@ -165,13 +183,32 @@ class ApiList { /* max-target-s */ SdkVersion::kS, }; - 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(uint32_t dex_flags) : dex_flags_(dex_flags) { + DCHECK_EQ(dex_flags_, (dex_flags_ & kValueBitMask) | (dex_flags_ & kDomainApiBitMask)); } - explicit ApiList(DomainApi val) : ApiList(Value::kInvalid, helper::ToBit(val)) {} + static ApiList FromValue(Value val) { + ApiList api_list(helper::ToUint(val)); + DCHECK(api_list.GetValue() == val); + DCHECK_EQ(api_list.GetDomainApis(), 0u); + return api_list; + } + + // Returns an ApiList with only a DomainApi bit set - the Value is invalid. It + // can be Combine'd with another ApiList with a Value to produce a valid combination. + static ApiList FromDomainApi(DomainApi domain_api) { + ApiList api_list(helper::ToUint(Value::kInvalid) | helper::ToBit(domain_api)); + DCHECK(api_list.GetValue() == Value::kInvalid); + DCHECK_EQ(api_list.GetDomainApis(), helper::ToBit(domain_api)); + return api_list; + } + + static ApiList FromValueAndDomainApis(Value val, uint32_t domain_apis) { + ApiList api_list(helper::ToUint(val) | domain_apis); + DCHECK(api_list.GetValue() == val); + DCHECK_EQ(api_list.GetDomainApis(), domain_apis); + return api_list; + } Value GetValue() const { uint32_t value = (dex_flags_ & kValueBitMask); @@ -190,47 +227,80 @@ class ApiList { uint32_t GetDomainApis() const { return (dex_flags_ & kDomainApiBitMask); } - uint32_t dex_flags_; - - public: - ApiList() : ApiList(Value::kInvalid) {} + // In order to correctly handle flagged changes from Unsupported to the Sdk, where both will be + // set when the flag is enabled, consider Sdk to take precedence over any form of unsupported. + // Note, this is not necessary in the inverse direction, because API flagging does not currently + // support API removal. Moving from the blocklist to unsupported is also a case we don't have to + // consider. + // If this is true, the conflict resolves to Value::kSdk. + static bool IsConflictingFlagsAcceptable(Value x, Value y) { + const auto predicate_non_symmetric = [](auto l, auto r) { + if (l != Value::kSdk) { + return false; + } + switch (r) { + case Value::kSdk: + case Value::kUnsupported: + case Value::kMaxTargetO: + case Value::kMaxTargetP: + case Value::kMaxTargetQ: + case Value::kMaxTargetR: + case Value::kMaxTargetS: + return true; + default: + return false; + } + }; + return predicate_non_symmetric(x, y) || predicate_non_symmetric(y, x); + } - explicit ApiList(uint32_t dex_flags) : dex_flags_(dex_flags) { - DCHECK_EQ(dex_flags_, (dex_flags_ & kValueBitMask) | (dex_flags_ & kDomainApiBitMask)); + // 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) || + IsConflictingFlagsAcceptable(val1, val2); } + public: // Helpers for conveniently constructing ApiList instances. - static ApiList Sdk() { return ApiList(Value::kSdk); } - static ApiList Unsupported() { return ApiList(Value::kUnsupported); } - static ApiList Blocked() { return ApiList(Value::kBlocked); } - static ApiList MaxTargetO() { return ApiList(Value::kMaxTargetO); } - static ApiList MaxTargetP() { return ApiList(Value::kMaxTargetP); } - static ApiList MaxTargetQ() { return ApiList(Value::kMaxTargetQ); } - static ApiList MaxTargetR() { return ApiList(Value::kMaxTargetR); } - static ApiList MaxTargetS() { return ApiList(Value::kMaxTargetS); } - static ApiList CorePlatformApi() { return ApiList(DomainApi::kCorePlatformApi); } - static ApiList TestApi() { return ApiList(DomainApi::kTestApi); } + static ApiList Sdk() { return FromValue(Value::kSdk); } + static ApiList Unsupported() { return FromValue(Value::kUnsupported); } + static ApiList Blocked() { return FromValue(Value::kBlocked); } + static ApiList MaxTargetO() { return FromValue(Value::kMaxTargetO); } + static ApiList MaxTargetP() { return FromValue(Value::kMaxTargetP); } + static ApiList MaxTargetQ() { return FromValue(Value::kMaxTargetQ); } + static ApiList MaxTargetR() { return FromValue(Value::kMaxTargetR); } + static ApiList MaxTargetS() { return FromValue(Value::kMaxTargetS); } + static ApiList Invalid() { return FromValue(Value::kInvalid); } + static ApiList CorePlatformApi() { return FromDomainApi(DomainApi::kCorePlatformApi); } + static ApiList TestApi() { return FromDomainApi(DomainApi::kTestApi); } uint32_t GetDexFlags() const { return dex_flags_; } - uint32_t GetIntValue() const { return helper::ToUint(GetValue()) - helper::ToUint(Value::kMin); } + uint32_t GetIntValue() const { return helper::ToUint(GetValue()); } + + static ApiList FromDexFlags(uint32_t dex_flags) { return ApiList(dex_flags); } + + static ApiList FromIntValue(uint32_t int_val) { + return FromValue(helper::GetEnumAt<Value>(int_val)); + } // 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(helper::GetEnumAt<Value>(i)); + return FromIntValue(i); } } for (uint32_t i = 0; i < kDomainApiCount; ++i) { if (str == kDomainApiNames[i]) { - return ApiList(helper::GetEnumAt<DomainApi>(i)); + return FromDomainApi(helper::GetEnumAt<DomainApi>(i)); } } if (str == kFutureValueName) { - static_assert(helper::ToUint(Value::kMax) + 1 < helper::ToUint(Value::kInvalid)); - return ApiList(helper::ToUint(Value::kMax) + 1); + return FromValue(Value::kFuture); } - return ApiList(); + return Invalid(); } // Parses a vector of flag names into a single ApiList value. If successful, @@ -238,7 +308,7 @@ class ApiList { static bool FromNames(std::vector<std::string>::iterator begin, std::vector<std::string>::iterator end, /* out */ ApiList* out_api_list) { - ApiList api_list; + ApiList api_list = Invalid(); for (std::vector<std::string>::iterator it = begin; it != end; it++) { ApiList current = FromName(*it); if (current.IsEmpty() || !api_list.CanCombineWith(current)) { @@ -250,7 +320,7 @@ class ApiList { } return false; } - api_list |= current; + api_list = Combine(api_list, current); } if (out_api_list != nullptr) { *out_api_list = api_list; @@ -260,71 +330,36 @@ class ApiList { bool operator==(const ApiList& other) const { return dex_flags_ == other.dex_flags_; } bool operator!=(const ApiList& other) const { return !(*this == other); } - bool operator<(const ApiList& other) const { return dex_flags_ < other.dex_flags_; } - bool operator>(const ApiList& other) const { return dex_flags_ > other.dex_flags_; } - // In order to correctly handle flagged changes from Unsupported to the Sdk, where both will be - // set when the flag is enabled, consider Sdk to take precedence over any form of unsupported. - // Note, this is not necessary in the inverse direction, because API flagging does not currently - // support API removal. Moving from the blocklist to unsupported is also a case we don't have to - // consider. - // If this is true, the conflict resolves to Value::kSdk. - static bool is_conflicting_flags_acceptable(Value x, Value y) { - const auto predicate_non_symmetric = [] (auto l, auto r) { - if (l != Value::kSdk) return false; - switch (r) { - case Value::kSdk: - case Value::kUnsupported: - case Value::kMaxTargetO: - case Value::kMaxTargetP: - case Value::kMaxTargetQ: - case Value::kMaxTargetR: - case Value::kMaxTargetS: - return true; - default: - return false; - } - }; - return predicate_non_symmetric(x, y) || predicate_non_symmetric(y, x); - } - - // 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) || - is_conflicting_flags_acceptable(val1, val2); - } + // The order doesn't have any significance - only for ordering in containers. + bool operator<(const ApiList& other) const { return dex_flags_ < other.dex_flags_; } - // Combine two ApiList instances. - ApiList operator|(const ApiList& other) { + // Combine two ApiList instances. The returned value has the union of the API + // domains. Values are mutually exclusive, so they either have to be identical + // or one of them can be safely ignored, which includes being kInvalid. + static ApiList Combine(const ApiList& api1, const ApiList& api2) { // DomainApis are not mutually exclusive. Simply OR them. - const uint32_t domain_apis = GetDomainApis() | other.GetDomainApis(); + // TODO: This is suspect since the app domain doesn't have any bit and hence + // implicitly disappears if OR'ed with any other domain. + const uint32_t domain_apis = api1.GetDomainApis() | api2.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(); + const Value val1 = api1.GetValue(); + const Value val2 = api2.GetValue(); if (val1 == val2) { - return ApiList(val1, domain_apis); + return FromValueAndDomainApis(val1, domain_apis); } else if (val1 == Value::kInvalid) { - return ApiList(val2, domain_apis); + return FromValueAndDomainApis(val2, domain_apis); } else if (val2 == Value::kInvalid) { - return ApiList(val1, domain_apis); - } else if (is_conflicting_flags_acceptable(val1, val2)) { - return ApiList(Value::kSdk, domain_apis); + return FromValueAndDomainApis(val1, domain_apis); + } else if (IsConflictingFlagsAcceptable(val1, val2)) { + return FromValueAndDomainApis(Value::kSdk, domain_apis); } else { - LOG(FATAL) << "Invalid combination of values " << Dumpable(ApiList(val1)) - << " and " << Dumpable(ApiList(val2)); + LOG(FATAL) << "Invalid combination of values " << Dumpable(FromValue(val1)) << " and " + << Dumpable(FromValue(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())) && @@ -338,13 +373,9 @@ class ApiList { bool IsEmpty() const { return (GetValue() == Value::kInvalid) && (GetDomainApis() == 0); } // Returns true if the ApiList is on blocklist. - bool IsBlocked() const { - return GetValue() == Value::kBlocked; - } + bool IsBlocked() const { return GetValue() == Value::kBlocked; } - bool IsSdkApi() const { - return GetValue() == Value::kSdk; - } + bool IsSdkApi() const { return GetValue() == Value::kSdk; } // Returns true if the ApiList is a test API. bool IsTestApi() const { diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h index 5979f86e8d..663b75cf86 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::ApiList(hiddenapi_flags_).IsValid()); + DCHECK(hiddenapi::ApiList::FromDexFlags(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::ApiList(hiddenapi_flags_).IsValid()); + DCHECK(hiddenapi::ApiList::FromDexFlags(hiddenapi_flags_).IsValid()); } } diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc index f0fe14fb4d..3f2fd627db 100644 --- a/libdexfile/dex/dex_file_verifier.cc +++ b/libdexfile/dex/dex_file_verifier.cc @@ -2683,7 +2683,7 @@ bool DexFileVerifier::CheckInterHiddenapiClassData() { failure = true; return; } - if (!hiddenapi::ApiList(decoded_flags).IsValid()) { + if (!hiddenapi::ApiList::FromDexFlags(decoded_flags).IsValid()) { ErrorStringPrintf("Hiddenapi class data flags invalid (%u) for %s %i", decoded_flags, member_type, member.GetIndex()); failure = true; diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index c7fb3ef7ff..0dc0b352f9 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -354,7 +354,7 @@ void MemberSignature::Dump(std::ostream& os) const { } void MemberSignature::LogAccessToLogcat(AccessMethod access_method, - hiddenapi::ApiList list, + ApiList api_list, bool access_denied, uint32_t runtime_flags, const AccessContext& caller_context, @@ -368,10 +368,10 @@ void MemberSignature::LogAccessToLogcat(AccessMethod access_method, << "hiddenapi: Accessing hidden " << (type_ == kField ? "field " : "method ") << Dumpable<MemberSignature>(*this) << " (runtime_flags=" << FormatHiddenApiRuntimeFlags(runtime_flags) - << ", domain=" << callee_context.GetDomain() << ", api=" << list << ") from " + << ", domain=" << callee_context.GetDomain() << ", api=" << api_list << ") from " << caller_context << " (domain=" << caller_context.GetDomain() << ") using " << access_method << (access_denied ? ": denied" : ": allowed"); - if (access_denied && list.IsTestApi()) { + if (access_denied && api_list.IsTestApi()) { // see b/177047045 for more details about test api access getting denied LOG(WARNING) << "hiddenapi: If this is a platform test consider enabling " << "VMRuntime.ALLOW_TEST_API_ACCESS change id for this package."; @@ -533,8 +533,7 @@ uint32_t GetDexFlags(T* member) REQUIRES_SHARED(Locks::mutator_lock_) { ObjPtr<mirror::Class> declaring_class = member->GetDeclaringClass(); DCHECK(!declaring_class.IsNull()) << "Attempting to access a runtime method"; - ApiList flags; - DCHECK(!flags.IsValid()); + ApiList flags = ApiList::Invalid(); // 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 @@ -556,7 +555,7 @@ uint32_t GetDexFlags(T* member) REQUIRES_SHARED(Locks::mutator_lock_) { uint32_t member_index = GetMemberDexIndex(member); auto fn_visit = [&](const AccessorType& dex_member) { if (dex_member.GetIndex() == member_index) { - flags = ApiList(dex_member.GetHiddenapiFlags()); + flags = ApiList::FromDexFlags(dex_member.GetHiddenapiFlags()); } }; VisitMembers(declaring_class->GetDexFile(), *class_def, fn_visit); @@ -576,7 +575,7 @@ 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 = ApiList(dex_member.GetHiddenapiFlags()); + flags = ApiList::FromDexFlags(dex_member.GetHiddenapiFlags()); } }; VisitMembers(*original_dex, original_class_def, fn_visit); @@ -811,7 +810,7 @@ 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(detail::GetDexFlags(member)); + ApiList api_list = ApiList::FromDexFlags(detail::GetDexFlags(member)); DCHECK(api_list.IsValid()); // Member is hidden and caller is not exempted. Enter slow path. diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index d2a6cdcbd3..7d7bd75fc4 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -266,7 +266,7 @@ inline ArtMethod* GetInterfaceMemberIfProxy(ArtMethod* method) ALWAYS_INLINE inline uint32_t CreateRuntimeFlags_Impl(uint32_t dex_flags) { uint32_t runtime_flags = 0u; - ApiList api_list(dex_flags); + ApiList api_list = ApiList::FromDexFlags(dex_flags); DCHECK(api_list.IsValid()); if (api_list.Contains(ApiList::Sdk())) { diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc index 5a2ee55723..39cca7cce6 100644 --- a/runtime/hidden_api_test.cc +++ b/runtime/hidden_api_test.cc @@ -202,6 +202,11 @@ class HiddenApiTest : public CommonRuntimeTest { hiddenapi::AccessMethod::kCheck); } + bool ShouldDenyAccess(hiddenapi::ApiList list1, hiddenapi::ApiList list2) + REQUIRES_SHARED(Locks::mutator_lock_) { + return ShouldDenyAccess(hiddenapi::ApiList::Combine(list1, list2)); + } + void TestLocation(const std::string& location, hiddenapi::Domain expected_domain) { // Create a temp file with a unique name based on `location` to isolate tests // that may run in parallel. b/238730923 @@ -382,59 +387,63 @@ TEST_F(HiddenApiTest, CheckTestApiEnforcement) { runtime_->SetTargetSdkVersion( static_cast<uint32_t>(hiddenapi::ApiList::MaxTargetR().GetMaxAllowedSdkVersion()) + 1); + // clang-format off + // Default case where all TestApis are treated like non-TestApi. runtime_->SetTestApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled); SetChangeIdState(kAllowTestApiAccess, false); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::Sdk()), false); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::Sdk()), false); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::Unsupported()), false); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::Unsupported()), false); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::MaxTargetR()), true); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::MaxTargetR()), true); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::MaxTargetQ()), true); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::MaxTargetQ()), true); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::MaxTargetP()), true); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::MaxTargetP()), true); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::MaxTargetO()), true); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::MaxTargetO()), true); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::Blocked()), true); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::Blocked()), true); // A case where we want to allow access to TestApis. runtime_->SetTestApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDisabled); SetChangeIdState(kAllowTestApiAccess, false); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::Sdk()), false); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::Sdk()), false); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::Unsupported()), false); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::Unsupported()), false); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::MaxTargetR()), false); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::MaxTargetR()), false); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::MaxTargetQ()), false); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::MaxTargetQ()), false); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::MaxTargetP()), false); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::MaxTargetP()), false); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::MaxTargetO()), false); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::MaxTargetO()), false); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::Blocked()), false); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::Blocked()), false); // A second case where we want to allow access to TestApis. runtime_->SetTestApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled); SetChangeIdState(kAllowTestApiAccess, true); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::Sdk()), false); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::Sdk()), false); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::Unsupported()), false); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::Unsupported()), false); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::MaxTargetR()), false); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::MaxTargetR()), false); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::MaxTargetQ()), false); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::MaxTargetQ()), false); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::MaxTargetP()), false); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::MaxTargetP()), false); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::MaxTargetO()), false); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::MaxTargetO()), false); ASSERT_EQ( - ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::Blocked()), false); + ShouldDenyAccess(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::Blocked()), false); + + // clang-format on } TEST_F(HiddenApiTest, CheckMembersRead) { diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 7bc0826424..fe923cbaa6 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -2298,7 +2298,8 @@ static bool IsInterfaceMethodAccessible(ArtMethod* interface_method) REQUIRES_SHARED(Locks::mutator_lock_) { // If the interface method is part of the public SDK, return it. if ((hiddenapi::GetRuntimeFlags(interface_method) & kAccPublicApi) != 0) { - hiddenapi::ApiList api_list(hiddenapi::detail::GetDexFlags(interface_method)); + hiddenapi::ApiList api_list = + hiddenapi::ApiList::FromDexFlags(hiddenapi::detail::GetDexFlags(interface_method)); // The kAccPublicApi flag is also used as an optimization to avoid // other hiddenapi checks to always go on the slow path. Therefore, we // need to check here if the method is in the SDK list. diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc index f5a8d58374..f5d59b9f39 100644 --- a/tools/hiddenapi/hiddenapi.cc +++ b/tools/hiddenapi/hiddenapi.cc @@ -898,7 +898,7 @@ class HiddenApi final { CHECK(!force_assign_all_ || api_list_found) << "Could not find hiddenapi flags for dex entry: " << signature; if (api_list_found && it->second.GetIntValue() > max_hiddenapi_level_.GetIntValue()) { - ApiList without_domain(it->second.GetIntValue()); + ApiList without_domain = ApiList::FromDexFlags(it->second.GetIntValue()); LOG(ERROR) << "Hidden api flag " << without_domain << " for member " << signature << " in " << input_path << " exceeds maximum allowable flag " << max_hiddenapi_level_; @@ -956,7 +956,7 @@ class HiddenApi final { CHECK(api_flag_map.find(signature) == api_flag_map.end()) << path << ":" << line_number << ": Duplicate entry: " << signature << kErrorHelp; - ApiList membership; + ApiList membership = ApiList::Invalid(); std::vector<std::string>::iterator apiListBegin = values.begin() + 1; std::vector<std::string>::iterator apiListEnd = values.end(); @@ -1098,7 +1098,7 @@ class HiddenApi final { // // By default this returns a GetIntValue() that is guaranteed to be bigger than // any valid value returned by GetIntValue(). - ApiList max_hiddenapi_level_; + ApiList max_hiddenapi_level_ = ApiList::Invalid(); // Whether the input is only a fragment of the whole bootclasspath and may // not include a complete set of classes. That requires the tool to ignore missing diff --git a/tools/hiddenapi/hiddenapi_test.cc b/tools/hiddenapi/hiddenapi_test.cc index 3236dddfd3..940a2630d9 100644 --- a/tools/hiddenapi/hiddenapi_test.cc +++ b/tools/hiddenapi/hiddenapi_test.cc @@ -190,7 +190,7 @@ class HiddenApiTest : public CommonRuntimeTest { const uint32_t actual_visibility = field.GetAccessFlags() & kAccVisibilityFlags; CHECK_EQ(actual_visibility, expected_visibility) << "Field " << name << " in class " << accessor.GetDescriptorView(); - return hiddenapi::ApiList(field.GetHiddenapiFlags()); + return hiddenapi::ApiList::FromDexFlags(field.GetHiddenapiFlags()); } } @@ -219,7 +219,7 @@ class HiddenApiTest : public CommonRuntimeTest { const uint32_t actual_visibility = method.GetAccessFlags() & kAccVisibilityFlags; CHECK_EQ(actual_visibility, expected_visibility) << "Method " << name << " in class " << accessor.GetDescriptorView(); - return hiddenapi::ApiList(method.GetHiddenapiFlags()); + return hiddenapi::ApiList::FromDexFlags(method.GetHiddenapiFlags()); } } @@ -700,8 +700,9 @@ TEST_F(HiddenApiTest, InstanceFieldCorePlatformApiMatch) { << "LMain;->ifield:I,unsupported,core-platform-api" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::CorePlatformApi() | - hiddenapi::ApiList::Unsupported(), GetIFieldHiddenFlags(*dex_file)); + ASSERT_EQ(hiddenapi::ApiList::Combine(hiddenapi::ApiList::CorePlatformApi(), + hiddenapi::ApiList::Unsupported()), + GetIFieldHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceFieldTestApiMatch) { @@ -712,8 +713,9 @@ TEST_F(HiddenApiTest, InstanceFieldTestApiMatch) { << "LMain;->ifield:I,unsupported,test-api" << std::endl; auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex); ASSERT_NE(dex_file.get(), nullptr); - ASSERT_EQ(hiddenapi::ApiList::TestApi() - | hiddenapi::ApiList::Unsupported(), GetIFieldHiddenFlags(*dex_file)); + ASSERT_EQ( + hiddenapi::ApiList::Combine(hiddenapi::ApiList::TestApi(), hiddenapi::ApiList::Unsupported()), + GetIFieldHiddenFlags(*dex_file)); } TEST_F(HiddenApiTest, InstanceFieldUnknownFlagMatch) { diff --git a/tools/veridex/api_list_filter.h b/tools/veridex/api_list_filter.h index 58065db2b2..c8e6aaa378 100644 --- a/tools/veridex/api_list_filter.h +++ b/tools/veridex/api_list_filter.h @@ -17,9 +17,10 @@ #ifndef ART_TOOLS_VERIDEX_API_LIST_FILTER_H_ #define ART_TOOLS_VERIDEX_API_LIST_FILTER_H_ -#include <algorithm> #include <android-base/strings.h> +#include <set> + #include "base/hiddenapi_flags.h" namespace art { @@ -46,10 +47,10 @@ class ApiListFilter { } if (include_invalid_list) { - lists_.push_back(hiddenapi::ApiList()); + lists_.push_back(hiddenapi::ApiList::Invalid()); } for (size_t i = 0; i < hiddenapi::ApiList::kValueCount; ++i) { - hiddenapi::ApiList list = hiddenapi::ApiList(i); + hiddenapi::ApiList list = hiddenapi::ApiList::FromIntValue(i); if (exclude_set.find(list) == exclude_set.end()) { lists_.push_back(list); } diff --git a/tools/veridex/hidden_api.cc b/tools/veridex/hidden_api.cc index 4156aa954b..1e0e303c57 100644 --- a/tools/veridex/hidden_api.cc +++ b/tools/veridex/hidden_api.cc @@ -34,7 +34,7 @@ HiddenApi::HiddenApi(const char* filename, const ApiListFilter& api_list_filter) std::vector<std::string> values = android::base::Split(str, ","); const std::string& signature = values[0]; - hiddenapi::ApiList membership; + hiddenapi::ApiList membership = hiddenapi::ApiList::Invalid(); bool success = hiddenapi::ApiList::FromNames(values.begin() + 1, values.end(), &membership); if (!success) { LOG(ERROR) << "Unknown ApiList flag: " << str; diff --git a/tools/veridex/hidden_api.h b/tools/veridex/hidden_api.h index a8301743aa..12082c3c70 100644 --- a/tools/veridex/hidden_api.h +++ b/tools/veridex/hidden_api.h @@ -45,7 +45,7 @@ class HiddenApi { hiddenapi::ApiList GetApiList(const std::string& name) const { auto it = api_list_.find(name); - return (it == api_list_.end()) ? hiddenapi::ApiList() : it->second; + return (it == api_list_.end()) ? hiddenapi::ApiList::Invalid() : it->second; } bool ShouldReport(const std::string& signature) const { diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc index 9c7bd78cff..ec2e96168d 100644 --- a/tools/veridex/veridex.cc +++ b/tools/veridex/veridex.cc @@ -270,9 +270,9 @@ class Veridex { os << stats.count << " hidden API(s) used: " << stats.linking_count << " linked against, " << stats.reflection_count << " through reflection" << std::endl; - DumpApiListStats(os, stats, hiddenapi::ApiList(), api_list_filter); + DumpApiListStats(os, stats, hiddenapi::ApiList::Invalid(), api_list_filter); for (size_t i = 0; i < hiddenapi::ApiList::kValueCount; ++i) { - DumpApiListStats(os, stats, hiddenapi::ApiList(i), api_list_filter); + DumpApiListStats(os, stats, hiddenapi::ApiList::FromIntValue(i), api_list_filter); } } |