1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef ART_LIBARTBASE_BASE_HIDDENAPI_FLAGS_H_ 18 #define ART_LIBARTBASE_BASE_HIDDENAPI_FLAGS_H_ 19 20 #include <android-base/logging.h> 21 22 #include <vector> 23 24 #include "base/bit_utils.h" 25 #include "base/dumpable.h" 26 #include "base/hiddenapi_stubs.h" 27 #include "base/macros.h" 28 #include "sdk_version.h" 29 30 namespace art { 31 namespace hiddenapi { 32 33 // Helper methods used inside ApiList. These were moved outside of the ApiList 34 // class so that they can be used in static_asserts. If they were inside, they 35 // would be part of an unfinished type. 36 namespace helper { 37 // Casts enum value to uint32_t. 38 template<typename T> ToUint(T val)39 constexpr uint32_t ToUint(T val) { return static_cast<uint32_t>(val); } 40 41 // Returns uint32_t with one bit set at an index given by an enum value. 42 template<typename T> ToBit(T val)43 constexpr uint32_t ToBit(T val) { return 1u << ToUint(val); } 44 45 // Returns a bit mask with `size` least significant bits set. BitMask(uint32_t size)46 constexpr uint32_t BitMask(uint32_t size) { return (1u << size) - 1; } 47 48 // Returns a bit mask formed from an enum defining kMin and kMax. The values 49 // are assumed to be indices of min/max bits and the resulting bitmask has 50 // bits [kMin, kMax] set. 51 template<typename T> BitMask()52 constexpr uint32_t BitMask() { 53 return BitMask(ToUint(T::kMax) + 1) & (~BitMask(ToUint(T::kMin))); 54 } 55 56 // Returns true if `val` is a bitwise subset of `mask`. MatchesBitMask(uint32_t val,uint32_t mask)57 constexpr bool MatchesBitMask(uint32_t val, uint32_t mask) { return (val & mask) == val; } 58 59 // Returns true if the uint32_t value of `val` is a bitwise subset of `mask`. 60 template<typename T> MatchesBitMask(T val,uint32_t mask)61 constexpr bool MatchesBitMask(T val, uint32_t mask) { return MatchesBitMask(ToUint(val), mask); } 62 63 // Returns the number of values defined in an enum, assuming the enum defines 64 // kMin and kMax and no integer values are skipped between them. 65 template<typename T> NumValues()66 constexpr uint32_t NumValues() { return ToUint(T::kMax) - ToUint(T::kMin) + 1; } 67 68 // Returns enum value at position i from enum list. 69 template <typename T> GetEnumAt(uint32_t i)70 constexpr T GetEnumAt(uint32_t i) { 71 return static_cast<T>(ToUint(T::kMin) + i); 72 } 73 74 } // namespace helper 75 76 /* 77 * This class represents the information whether a field/method is in 78 * public API (SDK) or if it isn't, apps targeting which SDK 79 * versions are allowed to access it. 80 */ 81 class ApiList { 82 private: 83 // The representation in dex_flags_ is a combination of a Value in the lowest 84 // kValueBitSize bits, and bit flags corresponding to DomainApi in bits above 85 // that. 86 uint32_t dex_flags_; 87 88 // Number of bits reserved for Value in dex flags, and the corresponding bit mask. 89 static constexpr uint32_t kValueBitSize = 4; 90 static constexpr uint32_t kValueBitMask = helper::BitMask(kValueBitSize); 91 92 enum class Value : uint32_t { 93 // Values independent of target SDK version of app 94 kSdk = 0, 95 kUnsupported = 1, // @UnsupportedAppUsage 96 kBlocked = 2, 97 98 // Values dependent on target SDK version of app. Put these last as 99 // their list will be extended in future releases. 100 // The max release code implicitly includes all maintenance releases, 101 // e.g. MaxTargetO is accessible to targetSdkVersion <= 27 (O_MR1). 102 kMaxTargetO = 3, 103 kMaxTargetP = 4, 104 kMaxTargetQ = 5, 105 kMaxTargetR = 6, 106 kMaxTargetS = 7, 107 108 // Invalid value. Does not imply the DomainApi is invalid. 109 kInvalid = (static_cast<uint32_t>(-1) & kValueBitMask), 110 111 kMin = kSdk, 112 kMax = kMaxTargetS, 113 kFuture = kMax + 1, // Only for testing 114 }; 115 116 // Additional bit flags after the first kValueBitSize bits in dex flags. These 117 // are used for domain-specific APIs. The app domain is the default when no 118 // bits are set. 119 enum class DomainApi : uint32_t { 120 kCorePlatformApi = kValueBitSize, 121 kTestApi = kValueBitSize + 1, 122 123 // Special values 124 kMin = kCorePlatformApi, 125 kMax = kTestApi, 126 }; 127 128 // Bit mask of all domain API flags. 129 static constexpr uint32_t kDomainApiBitMask = helper::BitMask<DomainApi>(); 130 131 // Check that Values fit in the designated number of bits. 132 static_assert(kValueBitSize >= MinimumBitsToStore(helper::ToUint(Value::kMax)), 133 "Not enough bits to store all ApiList values"); 134 135 // Check that all Values are covered by kValueBitMask. 136 static_assert(helper::MatchesBitMask(Value::kMin, kValueBitMask)); 137 static_assert(helper::MatchesBitMask(Value::kMax, kValueBitMask)); 138 static_assert(helper::MatchesBitMask(Value::kFuture, kValueBitMask)); 139 static_assert(helper::MatchesBitMask(Value::kInvalid, kValueBitMask)); 140 141 // Check that there's no offset between Values and the corresponding uint32 142 // dex flags, so they can be converted between each other without any change. 143 static_assert(helper::ToUint(Value::kMin) == 0); 144 145 // Check that Value::kInvalid is larger than kFuture (which is larger than kMax). 146 static_assert(helper::ToUint(Value::kFuture) < helper::ToUint(Value::kInvalid)); 147 148 // Check that no DomainApi bit flag is covered by kValueBitMask. 149 static_assert((helper::ToBit(DomainApi::kMin) & kValueBitMask) == 0); 150 static_assert((helper::ToBit(DomainApi::kMax) & kValueBitMask) == 0); 151 152 // Names corresponding to Values. 153 static constexpr const char* kValueNames[] = { 154 "sdk", 155 "unsupported", 156 "blocked", 157 "max-target-o", 158 "max-target-p", 159 "max-target-q", 160 "max-target-r", 161 "max-target-s", 162 }; 163 164 // A magic marker used by tests to mimic a hiddenapi list which doesn't exist 165 // yet. 166 static constexpr const char* kFutureValueName = "max-target-future"; 167 168 // Names corresponding to DomainApis. 169 static constexpr const char* kDomainApiNames[] { 170 "core-platform-api", 171 "test-api", 172 }; 173 174 // Maximum SDK versions allowed to access ApiList of given Value. 175 static constexpr SdkVersion kMaxSdkVersions[] { 176 /* sdk */ SdkVersion::kMax, 177 /* unsupported */ SdkVersion::kMax, 178 /* blocklist */ SdkVersion::kMin, 179 /* max-target-o */ SdkVersion::kO_MR1, 180 /* max-target-p */ SdkVersion::kP, 181 /* max-target-q */ SdkVersion::kQ, 182 /* max-target-r */ SdkVersion::kR, 183 /* max-target-s */ SdkVersion::kS, 184 }; 185 ApiList(uint32_t dex_flags)186 explicit ApiList(uint32_t dex_flags) : dex_flags_(dex_flags) { 187 DCHECK_EQ(dex_flags_, (dex_flags_ & kValueBitMask) | (dex_flags_ & kDomainApiBitMask)); 188 } 189 FromValue(Value val)190 static ApiList FromValue(Value val) { 191 ApiList api_list(helper::ToUint(val)); 192 DCHECK(api_list.GetValue() == val); 193 DCHECK_EQ(api_list.GetDomainApis(), 0u); 194 return api_list; 195 } 196 197 // Returns an ApiList with only a DomainApi bit set - the Value is invalid. It 198 // can be Combine'd with another ApiList with a Value to produce a valid combination. FromDomainApi(DomainApi domain_api)199 static ApiList FromDomainApi(DomainApi domain_api) { 200 ApiList api_list(helper::ToUint(Value::kInvalid) | helper::ToBit(domain_api)); 201 DCHECK(api_list.GetValue() == Value::kInvalid); 202 DCHECK_EQ(api_list.GetDomainApis(), helper::ToBit(domain_api)); 203 return api_list; 204 } 205 FromValueAndDomainApis(Value val,uint32_t domain_apis)206 static ApiList FromValueAndDomainApis(Value val, uint32_t domain_apis) { 207 ApiList api_list(helper::ToUint(val) | domain_apis); 208 DCHECK(api_list.GetValue() == val); 209 DCHECK_EQ(api_list.GetDomainApis(), domain_apis); 210 return api_list; 211 } 212 GetValue()213 Value GetValue() const { 214 uint32_t value = (dex_flags_ & kValueBitMask); 215 216 // Treat all ones as invalid value 217 if (value == helper::ToUint(Value::kInvalid)) { 218 return Value::kInvalid; 219 } else if (value > helper::ToUint(Value::kMax)) { 220 // For future unknown flag values, return unsupported. 221 return Value::kUnsupported; 222 } else { 223 DCHECK_GE(value, helper::ToUint(Value::kMin)); 224 return static_cast<Value>(value); 225 } 226 } 227 GetDomainApis()228 uint32_t GetDomainApis() const { return (dex_flags_ & kDomainApiBitMask); } 229 230 // In order to correctly handle flagged changes from Unsupported to the Sdk, where both will be 231 // set when the flag is enabled, consider Sdk to take precedence over any form of unsupported. 232 // Note, this is not necessary in the inverse direction, because API flagging does not currently 233 // support API removal. Moving from the blocklist to unsupported is also a case we don't have to 234 // consider. 235 // If this is true, the conflict resolves to Value::kSdk. IsConflictingFlagsAcceptable(Value x,Value y)236 static bool IsConflictingFlagsAcceptable(Value x, Value y) { 237 const auto predicate_non_symmetric = [](auto l, auto r) { 238 if (l != Value::kSdk) { 239 return false; 240 } 241 switch (r) { 242 case Value::kSdk: 243 case Value::kUnsupported: 244 case Value::kMaxTargetO: 245 case Value::kMaxTargetP: 246 case Value::kMaxTargetQ: 247 case Value::kMaxTargetR: 248 case Value::kMaxTargetS: 249 return true; 250 default: 251 return false; 252 } 253 }; 254 return predicate_non_symmetric(x, y) || predicate_non_symmetric(y, x); 255 } 256 257 // Returns true if combining this ApiList with `other` will succeed. CanCombineWith(const ApiList & other)258 bool CanCombineWith(const ApiList& other) const { 259 const Value val1 = GetValue(); 260 const Value val2 = other.GetValue(); 261 return (val1 == val2) || (val1 == Value::kInvalid) || (val2 == Value::kInvalid) || 262 IsConflictingFlagsAcceptable(val1, val2); 263 } 264 265 public: 266 // Helpers for conveniently constructing ApiList instances. Sdk()267 static ApiList Sdk() { return FromValue(Value::kSdk); } Unsupported()268 static ApiList Unsupported() { return FromValue(Value::kUnsupported); } Blocked()269 static ApiList Blocked() { return FromValue(Value::kBlocked); } MaxTargetO()270 static ApiList MaxTargetO() { return FromValue(Value::kMaxTargetO); } MaxTargetP()271 static ApiList MaxTargetP() { return FromValue(Value::kMaxTargetP); } MaxTargetQ()272 static ApiList MaxTargetQ() { return FromValue(Value::kMaxTargetQ); } MaxTargetR()273 static ApiList MaxTargetR() { return FromValue(Value::kMaxTargetR); } MaxTargetS()274 static ApiList MaxTargetS() { return FromValue(Value::kMaxTargetS); } Invalid()275 static ApiList Invalid() { return FromValue(Value::kInvalid); } CorePlatformApi()276 static ApiList CorePlatformApi() { return FromDomainApi(DomainApi::kCorePlatformApi); } TestApi()277 static ApiList TestApi() { return FromDomainApi(DomainApi::kTestApi); } 278 GetDexFlags()279 uint32_t GetDexFlags() const { return dex_flags_; } GetIntValue()280 uint32_t GetIntValue() const { return helper::ToUint(GetValue()); } 281 FromDexFlags(uint32_t dex_flags)282 static ApiList FromDexFlags(uint32_t dex_flags) { return ApiList(dex_flags); } 283 FromIntValue(uint32_t int_val)284 static ApiList FromIntValue(uint32_t int_val) { 285 return FromValue(helper::GetEnumAt<Value>(int_val)); 286 } 287 288 // Returns the ApiList with a flag of a given name, or an empty ApiList if not matched. FromName(const std::string & str)289 static ApiList FromName(const std::string& str) { 290 for (uint32_t i = 0; i < kValueCount; ++i) { 291 if (str == kValueNames[i]) { 292 return FromIntValue(i); 293 } 294 } 295 for (uint32_t i = 0; i < kDomainApiCount; ++i) { 296 if (str == kDomainApiNames[i]) { 297 return FromDomainApi(helper::GetEnumAt<DomainApi>(i)); 298 } 299 } 300 if (str == kFutureValueName) { 301 return FromValue(Value::kFuture); 302 } 303 return Invalid(); 304 } 305 306 // Parses a vector of flag names into a single ApiList value. If successful, 307 // returns true and assigns the new ApiList to `out_api_list`. FromNames(std::vector<std::string>::iterator begin,std::vector<std::string>::iterator end,ApiList * out_api_list)308 static bool FromNames(std::vector<std::string>::iterator begin, 309 std::vector<std::string>::iterator end, 310 /* out */ ApiList* out_api_list) { 311 ApiList api_list = Invalid(); 312 for (std::vector<std::string>::iterator it = begin; it != end; it++) { 313 ApiList current = FromName(*it); 314 if (current.IsEmpty() || !api_list.CanCombineWith(current)) { 315 if (ApiStubs::IsStubsFlag(*it)) { 316 // Ignore flags which correspond to the stubs from where the api 317 // originates (i.e. system-api, test-api, public-api), as they are not 318 // relevant at runtime 319 continue; 320 } 321 return false; 322 } 323 api_list = Combine(api_list, current); 324 } 325 if (out_api_list != nullptr) { 326 *out_api_list = api_list; 327 } 328 return true; 329 } 330 331 bool operator==(const ApiList& other) const { return dex_flags_ == other.dex_flags_; } 332 bool operator!=(const ApiList& other) const { return !(*this == other); } 333 334 // The order doesn't have any significance - only for ordering in containers. 335 bool operator<(const ApiList& other) const { return dex_flags_ < other.dex_flags_; } 336 337 // Combine two ApiList instances. The returned value has the union of the API 338 // domains. Values are mutually exclusive, so they either have to be identical 339 // or one of them can be safely ignored, which includes being kInvalid. Combine(const ApiList & api1,const ApiList & api2)340 static ApiList Combine(const ApiList& api1, const ApiList& api2) { 341 // DomainApis are not mutually exclusive. Simply OR them. 342 // TODO: This is suspect since the app domain doesn't have any bit and hence 343 // implicitly disappears if OR'ed with any other domain. 344 const uint32_t domain_apis = api1.GetDomainApis() | api2.GetDomainApis(); 345 346 const Value val1 = api1.GetValue(); 347 const Value val2 = api2.GetValue(); 348 if (val1 == val2) { 349 return FromValueAndDomainApis(val1, domain_apis); 350 } else if (val1 == Value::kInvalid) { 351 return FromValueAndDomainApis(val2, domain_apis); 352 } else if (val2 == Value::kInvalid) { 353 return FromValueAndDomainApis(val1, domain_apis); 354 } else if (IsConflictingFlagsAcceptable(val1, val2)) { 355 return FromValueAndDomainApis(Value::kSdk, domain_apis); 356 } else { 357 LOG(FATAL) << "Invalid combination of values " << Dumpable(FromValue(val1)) << " and " 358 << Dumpable(FromValue(val2)); 359 UNREACHABLE(); 360 } 361 } 362 363 // Returns true if all flags set in `other` are also set in `this`. Contains(const ApiList & other)364 bool Contains(const ApiList& other) const { 365 return ((other.GetValue() == Value::kInvalid) || (GetValue() == other.GetValue())) && 366 helper::MatchesBitMask(other.GetDomainApis(), GetDomainApis()); 367 } 368 369 // Returns true whether the configuration is valid for runtime use. IsValid()370 bool IsValid() const { return GetValue() != Value::kInvalid; } 371 372 // Returns true when no ApiList is specified and no domain_api flags either. IsEmpty()373 bool IsEmpty() const { return (GetValue() == Value::kInvalid) && (GetDomainApis() == 0); } 374 375 // Returns true if the ApiList is on blocklist. IsBlocked()376 bool IsBlocked() const { return GetValue() == Value::kBlocked; } 377 IsSdkApi()378 bool IsSdkApi() const { return GetValue() == Value::kSdk; } 379 380 // Returns true if the ApiList is a test API. IsTestApi()381 bool IsTestApi() const { 382 return helper::MatchesBitMask(helper::ToBit(DomainApi::kTestApi), dex_flags_); 383 } 384 385 // Returns the maximum target SDK version allowed to access this ApiList. GetMaxAllowedSdkVersion()386 SdkVersion GetMaxAllowedSdkVersion() const { return kMaxSdkVersions[GetIntValue()]; } 387 Dump(std::ostream & os)388 void Dump(std::ostream& os) const { 389 bool is_first = true; 390 391 if (IsEmpty()) { 392 os << "invalid"; 393 return; 394 } 395 396 if (GetValue() != Value::kInvalid) { 397 os << kValueNames[GetIntValue()]; 398 is_first = false; 399 } 400 401 const uint32_t domain_apis = GetDomainApis(); 402 for (uint32_t i = 0; i < kDomainApiCount; i++) { 403 if (helper::MatchesBitMask(helper::ToBit(helper::GetEnumAt<DomainApi>(i)), domain_apis)) { 404 if (is_first) { 405 is_first = false; 406 } else { 407 os << ","; 408 } 409 os << kDomainApiNames[i]; 410 } 411 } 412 413 DCHECK_EQ(IsEmpty(), is_first); 414 } 415 416 // Number of valid enum values in Value. 417 static constexpr uint32_t kValueCount = helper::NumValues<Value>(); 418 // Number of valid enum values in DomainApi. 419 static constexpr uint32_t kDomainApiCount = helper::NumValues<DomainApi>(); 420 // Total number of possible enum values, including invalid, in Value. 421 static constexpr uint32_t kValueSize = (1u << kValueBitSize) + 1; 422 423 // Check min and max values are calculated correctly. 424 static_assert(Value::kMin == helper::GetEnumAt<Value>(0)); 425 static_assert(Value::kMax == helper::GetEnumAt<Value>(kValueCount - 1)); 426 427 static_assert(DomainApi::kMin == helper::GetEnumAt<DomainApi>(0)); 428 static_assert(DomainApi::kMax == helper::GetEnumAt<DomainApi>(kDomainApiCount - 1)); 429 }; 430 431 inline std::ostream& operator<<(std::ostream& os, ApiList value) { 432 value.Dump(os); 433 return os; 434 } 435 436 } // namespace hiddenapi 437 } // namespace art 438 439 440 #endif // ART_LIBARTBASE_BASE_HIDDENAPI_FLAGS_H_ 441