• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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