diff options
Diffstat (limited to 'libartbase/base/hiddenapi_flags.h')
-rw-r--r-- | libartbase/base/hiddenapi_flags.h | 241 |
1 files changed, 136 insertions, 105 deletions
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 { |