Merge changes from topics "coreapi-flag", "coreapi-stubs-csv"
* changes:
hiddenapi: Print warnings for @CorePlatformApi violations
hiddenapi: Support 'core-platform-api' flag
hiddenapi: Produce CSV for `list` command
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index ef2c9e4..7382a97 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -224,7 +224,9 @@
}
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_domain.h b/libartbase/base/hiddenapi_domain.h
new file mode 100644
index 0000000..4cbc22d
--- /dev/null
+++ b/libartbase/base/hiddenapi_domain.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBARTBASE_BASE_HIDDENAPI_DOMAIN_H_
+#define ART_LIBARTBASE_BASE_HIDDENAPI_DOMAIN_H_
+
+namespace art {
+namespace hiddenapi {
+
+// List of domains supported by the hidden API access checks. Domain with a lower
+// ordinal is considered more "trusted", i.e. always allowed to access members of
+// domains with a greater ordinal. Access checks are performed when code tries to
+// access a method/field from a more trusted domain than itself.
+enum class Domain {
+ kCorePlatform = 0,
+ kPlatform,
+ kApplication,
+};
+
+inline bool IsDomainMoreTrustedThan(Domain domainA, Domain domainB) {
+ return static_cast<uint32_t>(domainA) <= static_cast<uint32_t>(domainB);
+}
+
+} // namespace hiddenapi
+} // namespace art
+
+
+#endif // ART_LIBARTBASE_BASE_HIDDENAPI_DOMAIN_H_
diff --git a/libartbase/base/hiddenapi_flags.cc b/libartbase/base/hiddenapi_flags.cc
index 6caa75c..ea57cb7 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 8c1ffd5..c468723 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 @@
*/
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),
- kMinValue = kWhitelist,
- kMaxValue = kGreylistMaxP,
+ kInvalid = (static_cast<uint32_t>(-1) & kValueBitMask),
+ kMin = kWhitelist,
+ kMax = kGreylistMaxP,
};
- static constexpr const char* kNames[] = {
+ // 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
+ kMin = kCorePlatformApi,
+ kMax = kCorePlatformApi,
+ };
+
+ // 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 @@
"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,74 +142,167 @@
/* 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));
- }
- return Invalid();
- }
-
- // Returns the ApiList with a given name.
+ // 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 (IntValueType i = MinValue().GetIntValue(); i <= MaxValue().GetIntValue(); i++) {
- ApiList current = ApiList(static_cast<Value>(i));
- if (str == current.GetName()) {
- return current;
+ 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();
}
- bool operator==(const ApiList other) const { return value_ == other.value_; }
- bool operator!=(const ApiList other) const { return !(*this == other); }
-
- bool IsValid() const { return *this != Invalid(); }
-
- IntValueType GetIntValue() const {
- DCHECK(IsValid());
- return static_cast<IntValueType>(value_);
+ // 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;
+ }
+ if (out_api_list != nullptr) {
+ *out_api_list = api_list;
+ }
+ return true;
}
- const char* GetName() const { return IsValid() ? kNames[GetIntValue()]: kInvalidName; }
+ 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);
+ }
+
+ // 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();
+ }
+ }
+
+ 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())) &&
+ helper::MatchesBitMask(other.GetDomainApis(), GetDomainApis());
+ }
+
+ // 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/art_dex_file_loader.cc b/libdexfile/dex/art_dex_file_loader.cc
index 57e838f..a814b66 100644
--- a/libdexfile/dex/art_dex_file_loader.cc
+++ b/libdexfile/dex/art_dex_file_loader.cc
@@ -547,7 +547,7 @@
// that this will call `realpath`.
std::string path = DexFileLoader::GetDexCanonicalLocation(location.c_str());
if (dex_file != nullptr && LocationIsOnSystemFramework(path.c_str())) {
- dex_file->SetIsPlatformDexFile();
+ dex_file->SetHiddenapiDomain(hiddenapi::Domain::kPlatform);
}
return dex_file;
diff --git a/libdexfile/dex/art_dex_file_loader_test.cc b/libdexfile/dex/art_dex_file_loader_test.cc
index f9516db..8c9258b 100644
--- a/libdexfile/dex/art_dex_file_loader_test.cc
+++ b/libdexfile/dex/art_dex_file_loader_test.cc
@@ -340,7 +340,7 @@
ASSERT_GE(dex_files.size(), 1u);
for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
- ASSERT_FALSE(dex_file->IsPlatformDexFile());
+ ASSERT_NE(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
}
dex_files.clear();
@@ -368,7 +368,7 @@
ASSERT_GE(dex_files.size(), 1u);
for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
- ASSERT_FALSE(dex_file->IsPlatformDexFile());
+ ASSERT_NE(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
}
dex_files.clear();
@@ -396,7 +396,7 @@
ASSERT_GE(dex_files.size(), 1u);
for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
- ASSERT_TRUE(dex_file->IsPlatformDexFile());
+ ASSERT_EQ(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
}
dex_files.clear();
@@ -424,7 +424,7 @@
ASSERT_GT(dex_files.size(), 1u);
for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
- ASSERT_FALSE(dex_file->IsPlatformDexFile());
+ ASSERT_NE(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
}
dex_files.clear();
@@ -453,7 +453,7 @@
ASSERT_GT(dex_files.size(), 1u);
for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
- ASSERT_FALSE(dex_file->IsPlatformDexFile());
+ ASSERT_NE(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
}
dex_files.clear();
@@ -482,7 +482,7 @@
ASSERT_GT(dex_files.size(), 1u);
for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
- ASSERT_TRUE(dex_file->IsPlatformDexFile());
+ ASSERT_EQ(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
}
dex_files.clear();
diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h
index 8562d05..f224a29 100644
--- a/libdexfile/dex/class_accessor-inl.h
+++ b/libdexfile/dex/class_accessor-inl.h
@@ -69,7 +69,7 @@
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 @@
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.cc b/libdexfile/dex/dex_file.cc
index d3cdf13..39377a3 100644
--- a/libdexfile/dex/dex_file.cc
+++ b/libdexfile/dex/dex_file.cc
@@ -123,7 +123,7 @@
oat_dex_file_(oat_dex_file),
container_(std::move(container)),
is_compact_dex_(is_compact_dex),
- is_platform_dex_(false) {
+ hiddenapi_domain_(hiddenapi::Domain::kApplication) {
CHECK(begin_ != nullptr) << GetLocation();
CHECK_GT(size_, 0U) << GetLocation();
// Check base (=header) alignment.
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index a940a66..c7fbe78 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -24,6 +24,7 @@
#include <android-base/logging.h>
#include "base/globals.h"
+#include "base/hiddenapi_domain.h"
#include "base/macros.h"
#include "base/value_object.h"
#include "class_iterator.h"
@@ -754,13 +755,8 @@
ALWAYS_INLINE const StandardDexFile* AsStandardDexFile() const;
ALWAYS_INLINE const CompactDexFile* AsCompactDexFile() const;
- ALWAYS_INLINE bool IsPlatformDexFile() const {
- return is_platform_dex_;
- }
-
- ALWAYS_INLINE void SetIsPlatformDexFile() {
- is_platform_dex_ = true;
- }
+ hiddenapi::Domain GetHiddenapiDomain() const { return hiddenapi_domain_; }
+ void SetHiddenapiDomain(hiddenapi::Domain value) { hiddenapi_domain_ = value; }
bool IsInMainSection(const void* addr) const {
return Begin() <= addr && addr < Begin() + Size();
@@ -874,8 +870,7 @@
// If the dex file is a compact dex file. If false then the dex file is a standard dex file.
const bool is_compact_dex_;
- // If the dex file is located in /system/framework/.
- bool is_platform_dex_;
+ hiddenapi::Domain hiddenapi_domain_;
friend class DexFileLoader;
friend class DexFileVerifierTest;
diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc
index f376c4d..86a28e5 100644
--- a/libdexfile/dex/dex_file_verifier.cc
+++ b/libdexfile/dex/dex_file_verifier.cc
@@ -1630,7 +1630,7 @@
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 114c8e6..0c79c96 100644
--- a/libdexfile/dex/modifiers.h
+++ b/libdexfile/dex/modifiers.h
@@ -85,7 +85,7 @@
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 @@
// 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/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc
index 41e4291..a3e06e6 100644
--- a/openjdkjvmti/fixed_up_dex_file.cc
+++ b/openjdkjvmti/fixed_up_dex_file.cc
@@ -141,14 +141,12 @@
/*verify_checksum=*/false,
&error);
- if (new_dex_file == nullptr) {
+ if (new_dex_file == nullptr) {
LOG(ERROR) << "Unable to open dex file from memory for unquickening! error: " << error;
return nullptr;
}
- if (original.IsPlatformDexFile()) {
- const_cast<art::DexFile*>(new_dex_file.get())->SetIsPlatformDexFile();
- }
+ const_cast<art::DexFile*>(new_dex_file.get())->SetHiddenapiDomain(original.GetHiddenapiDomain());
DoDexUnquicken(*new_dex_file, original);
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index e273d94..07193b2 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -727,7 +727,7 @@
// 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 2d3493d..af5e67a 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -67,6 +67,19 @@
return os;
}
+static inline std::ostream& operator<<(std::ostream& os, const AccessContext& value)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!value.GetClass().IsNull()) {
+ std::string tmp;
+ os << value.GetClass()->GetDescriptor(&tmp);
+ } else if (value.GetDexFile() != nullptr) {
+ os << value.GetDexFile()->GetLocation();
+ } else {
+ os << "<unknown_caller>";
+ }
+ return os;
+}
+
namespace detail {
// Do not change the values of items in this enum, as they are written to the
@@ -267,9 +280,6 @@
}
}
-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 +312,10 @@
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 +326,15 @@
// 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,22 +350,30 @@
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>
-bool ShouldDenyAccessToMemberImpl(T* member,
- hiddenapi::ApiList api_list,
- AccessMethod access_method) {
+void MaybeReportCorePlatformApiViolation(T* member,
+ const AccessContext& caller_context,
+ AccessMethod access_method) {
+ if (access_method != AccessMethod::kNone) {
+ MemberSignature sig(member);
+ LOG(ERROR) << "CorePlatformApi violation: " << Dumpable<MemberSignature>(sig)
+ << " from " << caller_context << " using " << access_method;
+ }
+}
+
+template<typename T>
+bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method) {
DCHECK(member != nullptr);
Runtime* runtime = Runtime::Current();
@@ -414,14 +428,20 @@
return deny_access;
}
-// Need to instantiate this.
+// Need to instantiate these.
template uint32_t GetDexFlags<ArtField>(ArtField* member);
template uint32_t GetDexFlags<ArtMethod>(ArtMethod* member);
+template void MaybeReportCorePlatformApiViolation(ArtField* member,
+ const AccessContext& caller_context,
+ AccessMethod access_method);
+template void MaybeReportCorePlatformApiViolation(ArtMethod* member,
+ const AccessContext& caller_context,
+ AccessMethod access_method);
template bool ShouldDenyAccessToMemberImpl<ArtField>(ArtField* member,
- hiddenapi::ApiList api_list,
+ ApiList api_list,
AccessMethod access_method);
template bool ShouldDenyAccessToMemberImpl<ArtMethod>(ArtMethod* member,
- hiddenapi::ApiList api_list,
+ ApiList api_list,
AccessMethod access_method);
} // namespace detail
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 1a5e010..c73a710 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -52,50 +52,91 @@
kLinking,
};
-struct AccessContext {
+// Represents the API domain of a caller/callee.
+class AccessContext {
public:
- explicit AccessContext(bool is_trusted) : is_trusted_(is_trusted) {}
+ // Initialize to either the fully-trusted or fully-untrusted domain.
+ explicit AccessContext(bool is_trusted)
+ : klass_(nullptr),
+ dex_file_(nullptr),
+ domain_(ComputeDomain(is_trusted)) {}
- explicit AccessContext(ObjPtr<mirror::Class> klass) : is_trusted_(GetIsTrusted(klass)) {}
-
+ // Initialize from class loader and dex file (via dex cache).
AccessContext(ObjPtr<mirror::ClassLoader> class_loader, ObjPtr<mirror::DexCache> dex_cache)
- : is_trusted_(GetIsTrusted(class_loader, dex_cache)) {}
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ : klass_(nullptr),
+ dex_file_(GetDexFileFromDexCache(dex_cache)),
+ domain_(ComputeDomain(class_loader, dex_file_)) {}
- bool IsTrusted() const { return is_trusted_; }
+ // Initialize from Class.
+ explicit AccessContext(ObjPtr<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ : klass_(klass),
+ dex_file_(GetDexFileFromDexCache(klass->GetDexCache())),
+ domain_(ComputeDomain(klass, dex_file_)) {}
+
+ ObjPtr<mirror::Class> GetClass() const { return klass_; }
+ const DexFile* GetDexFile() const { return dex_file_; }
+ Domain GetDomain() const { return domain_; }
+
+ bool IsUntrustedDomain() const { return domain_ == Domain::kApplication; }
+
+ // Returns true if this domain is always allowed to access the domain of `callee`.
+ bool CanAlwaysAccess(const AccessContext& callee) const {
+ return IsDomainMoreTrustedThan(domain_, callee.domain_);
+ }
private:
- static bool GetIsTrusted(ObjPtr<mirror::ClassLoader> class_loader,
- ObjPtr<mirror::DexCache> dex_cache)
+ static const DexFile* GetDexFileFromDexCache(ObjPtr<mirror::DexCache> dex_cache)
REQUIRES_SHARED(Locks::mutator_lock_) {
- // Trust if the caller is in is boot class loader.
- if (class_loader.IsNull()) {
- return true;
- }
-
- // Trust if caller is in a platform dex file.
- if (!dex_cache.IsNull()) {
- const DexFile* dex_file = dex_cache->GetDexFile();
- if (dex_file != nullptr && dex_file->IsPlatformDexFile()) {
- return true;
- }
- }
-
- return false;
+ return dex_cache.IsNull() ? nullptr : dex_cache->GetDexFile();
}
- static bool GetIsTrusted(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK(!klass.IsNull());
+ static Domain ComputeDomain(bool is_trusted) {
+ return is_trusted ? Domain::kCorePlatform : Domain::kApplication;
+ }
- if (klass->ShouldSkipHiddenApiChecks() && Runtime::Current()->IsJavaDebuggable()) {
- // Class is known, it is marked trusted and we are in debuggable mode.
- return true;
+ static Domain ComputeDomain(ObjPtr<mirror::ClassLoader> class_loader, const DexFile* dex_file)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (dex_file == nullptr) {
+ return ComputeDomain(/* is_trusted= */ class_loader.IsNull());
}
+ Domain dex_domain = dex_file->GetHiddenapiDomain();
+ if (class_loader.IsNull() && dex_domain == Domain::kApplication) {
+ LOG(WARNING) << "DexFile " << dex_file->GetLocation() << " is in boot classpath "
+ << "but is assigned untrusted domain";
+ dex_domain = Domain::kPlatform;
+ }
+ return dex_domain;
+ }
+
+ static Domain ComputeDomain(ObjPtr<mirror::Class> klass, const DexFile* dex_file)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
// Check other aspects of the context.
- return GetIsTrusted(klass->GetClassLoader(), klass->GetDexCache());
+ Domain domain = ComputeDomain(klass->GetClassLoader(), dex_file);
+
+ if (domain == Domain::kApplication &&
+ klass->ShouldSkipHiddenApiChecks() &&
+ Runtime::Current()->IsJavaDebuggable()) {
+ // Class is known, it is marked trusted and we are in debuggable mode.
+ domain = ComputeDomain(/* is_trusted= */ true);
+ }
+
+ return domain;
}
- bool is_trusted_;
+ // Pointer to declaring class of the caller/callee (null if not provided).
+ // This is not safe across GC but we're only using this class for passing
+ // information about the caller to the access check logic and never retain
+ // the AccessContext instance beyond that.
+ const ObjPtr<mirror::Class> klass_;
+
+ // DexFile of the caller/callee (null if not provided).
+ const DexFile* const dex_file_;
+
+ // Computed domain of the caller/callee.
+ const Domain domain_;
};
class ScopedHiddenApiEnforcementPolicySetting {
@@ -169,6 +210,12 @@
uint32_t GetDexFlags(T* member) REQUIRES_SHARED(Locks::mutator_lock_);
template<typename T>
+void MaybeReportCorePlatformApiViolation(T* member,
+ const AccessContext& caller_context,
+ AccessMethod access_method)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+template<typename T>
bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -178,12 +225,17 @@
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)
@@ -304,44 +356,86 @@
AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(member != nullptr);
+ const uint32_t runtime_flags = GetRuntimeFlags(member);
// Exit early if member is public API. This flag is also set for non-boot class
// path fields/methods.
- if ((GetRuntimeFlags(member) & kAccPublicApi) != 0) {
+ if ((runtime_flags & kAccPublicApi) != 0) {
return false;
}
- // Exit early if access checks are completely disabled.
- if (Runtime::Current()->GetHiddenApiEnforcementPolicy() == EnforcementPolicy::kDisabled) {
+ // Determine which domain the caller and callee belong to.
+ // This can be *very* expensive. This is why ShouldDenyAccessToMember
+ // should not be called on every individual access.
+ const AccessContext caller_context = fn_get_access_context();
+ const AccessContext callee_context(member->GetDeclaringClass());
+
+ // Non-boot classpath callers should have exited early.
+ DCHECK(!callee_context.IsUntrustedDomain());
+
+ // Check if the caller is always allowed to access members in the callee context.
+ if (caller_context.CanAlwaysAccess(callee_context)) {
return false;
}
- // Check if caller is exempted from access checks.
- // This can be *very* expensive. Save it for last.
- if (fn_get_access_context().IsTrusted()) {
- return false;
+ // Check if this is platform accessing core platform. We may warn if `member` is
+ // not part of core platform API.
+ switch (caller_context.GetDomain()) {
+ case Domain::kApplication: {
+ DCHECK(!callee_context.IsUntrustedDomain());
+
+ // Exit early if access checks are completely disabled.
+ EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
+ if (policy == EnforcementPolicy::kDisabled) {
+ return false;
+ }
+
+ // 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));
+ DCHECK(api_list.IsValid());
+
+ // Member is hidden and caller is not exempted. Enter slow path.
+ return detail::ShouldDenyAccessToMemberImpl(member, api_list, access_method);
+ }
+
+ case Domain::kPlatform: {
+ DCHECK(callee_context.GetDomain() == Domain::kCorePlatform);
+
+ // Member is part of core platform API. Accessing it is allowed.
+ if ((runtime_flags & kAccCorePlatformApi) != 0) {
+ return false;
+ }
+
+ // Allow access if access checks are disabled.
+ EnforcementPolicy policy = Runtime::Current()->GetCorePlatformApiEnforcementPolicy();
+ if (policy == EnforcementPolicy::kDisabled) {
+ return false;
+ }
+
+ // Access checks are not disabled, report the violation.
+ detail::MaybeReportCorePlatformApiViolation(member, caller_context, access_method);
+
+ // Deny access if the policy is enabled.
+ return policy == EnforcementPolicy::kEnabled;
+ }
+
+ case Domain::kCorePlatform: {
+ LOG(FATAL) << "CorePlatform domain should be allowed to access all domains";
+ UNREACHABLE();
+ }
}
-
- // 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));
-
- // Member is hidden and caller is not exempted. Enter slow path.
- return detail::ShouldDenyAccessToMemberImpl(member, api_list, access_method);
}
// Helper method for callers where access context can be determined beforehand.
// Wraps AccessContext in a lambda and passes it to the real ShouldDenyAccessToMember.
template<typename T>
inline bool ShouldDenyAccessToMember(T* member,
- AccessContext access_context,
+ const AccessContext& access_context,
AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return ShouldDenyAccessToMember(
- member,
- [&] () REQUIRES_SHARED(Locks::mutator_lock_) { return access_context; },
- access_method);
+ return ShouldDenyAccessToMember(member, [&]() { return access_context; }, access_method);
}
} // namespace hiddenapi
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 4fa7271..72a8727 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -185,13 +185,13 @@
static ALWAYS_INLINE bool ShouldDenyAccessToMember(T* member, ShadowFrame* frame)
REQUIRES_SHARED(Locks::mutator_lock_) {
// All uses in this file are from reflection
- constexpr hiddenapi::AccessMethod access_method = hiddenapi::AccessMethod::kReflection;
+ constexpr hiddenapi::AccessMethod kAccessMethod = hiddenapi::AccessMethod::kReflection;
return hiddenapi::ShouldDenyAccessToMember(
member,
[&]() REQUIRES_SHARED(Locks::mutator_lock_) {
return hiddenapi::AccessContext(frame->GetMethod()->GetDeclaringClass());
},
- access_method);
+ kAccessMethod);
}
void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self,
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 52482b7..daf02fe 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -836,8 +836,9 @@
return;
}
+ // Assign core platform domain as the dex files are allowed to access all the other domains.
for (const DexFile* dex_file : dex_files) {
- const_cast<DexFile*>(dex_file)->SetIsPlatformDexFile();
+ const_cast<DexFile*>(dex_file)->SetHiddenapiDomain(hiddenapi::Domain::kCorePlatform);
}
}
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index d022c3b..4115791 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -130,7 +130,7 @@
// callers (hiddenapi_context).
template<typename T>
ALWAYS_INLINE static bool IsDiscoverable(bool public_only,
- hiddenapi::AccessContext access_context,
+ const hiddenapi::AccessContext& access_context,
T* member)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (public_only && ((member->GetAccessFlags() & kAccPublic) == 0)) {
@@ -508,8 +508,9 @@
}
static ALWAYS_INLINE inline bool MethodMatchesConstructor(
- ArtMethod* m, bool public_only, hiddenapi::AccessContext hiddenapi_context)
- REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtMethod* m,
+ bool public_only,
+ const hiddenapi::AccessContext& hiddenapi_context) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(m != nullptr);
return m->IsConstructor() &&
!m->IsStatic() &&
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index d537de5..4853187 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -273,6 +273,7 @@
is_low_memory_mode_(false),
safe_mode_(false),
hidden_api_policy_(hiddenapi::EnforcementPolicy::kDisabled),
+ core_platform_api_policy_(hiddenapi::EnforcementPolicy::kJustWarn),
dedupe_hidden_api_warnings_(true),
hidden_api_access_event_log_rate_(0),
dump_native_stack_on_sig_quit_(true),
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 1d1d0d3..ee2c514 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -539,6 +539,14 @@
return hidden_api_policy_;
}
+ void SetCorePlatformApiEnforcementPolicy(hiddenapi::EnforcementPolicy policy) {
+ core_platform_api_policy_ = policy;
+ }
+
+ hiddenapi::EnforcementPolicy GetCorePlatformApiEnforcementPolicy() const {
+ return core_platform_api_policy_;
+ }
+
void SetHiddenApiExemptions(const std::vector<std::string>& exemptions) {
hidden_api_exemptions_ = exemptions;
}
@@ -1075,23 +1083,17 @@
// Whether access checks on hidden API should be performed.
hiddenapi::EnforcementPolicy hidden_api_policy_;
+ // Whether access checks on core platform API should be performed.
+ hiddenapi::EnforcementPolicy core_platform_api_policy_;
+
// List of signature prefixes of methods that have been removed from the blacklist, and treated
// as if whitelisted.
std::vector<std::string> hidden_api_exemptions_;
- // Whether the application has used an API which is not restricted but we
- // should issue a warning about it.
- bool pending_hidden_api_warning_;
-
// Do not warn about the same hidden API access violation twice.
// This is only used for testing.
bool dedupe_hidden_api_warnings_;
- // Hidden API can print warnings into the log and/or set a flag read by the
- // framework to show a UI warning. If this flag is set, always set the flag
- // when there is a warning. This is only used for testing.
- bool always_set_hidden_api_warning_flag_;
-
// How often to log hidden API access to the event log. An integer between 0
// (never) and 0x10000 (always).
uint32_t hidden_api_access_event_log_rate_;
diff --git a/test/674-hiddenapi/hiddenapi-flags.csv b/test/674-hiddenapi/hiddenapi-flags.csv
index d875bdf..42626f7 100644
--- a/test/674-hiddenapi/hiddenapi-flags.csv
+++ b/test/674-hiddenapi/hiddenapi-flags.csv
@@ -1,30 +1,41 @@
+LNullaryConstructorBlacklistAndCorePlatformApi;-><init>()V,blacklist,core-platform-api
LNullaryConstructorBlacklist;-><init>()V,blacklist
LNullaryConstructorDarkGreylist;-><init>()V,greylist-max-o
LNullaryConstructorLightGreylist;-><init>()V,greylist
+LParentClass;->fieldPackageBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
LParentClass;->fieldPackageBlacklist:I,blacklist
LParentClass;->fieldPackageDarkGreylist:I,greylist-max-o
LParentClass;->fieldPackageLightGreylist:I,greylist
+LParentClass;->fieldPackageStaticBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
LParentClass;->fieldPackageStaticBlacklist:I,blacklist
LParentClass;->fieldPackageStaticDarkGreylist:I,greylist-max-o
LParentClass;->fieldPackageStaticLightGreylist:I,greylist
+LParentClass;->fieldPrivateBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
LParentClass;->fieldPrivateBlacklist:I,blacklist
LParentClass;->fieldPrivateDarkGreylist:I,greylist-max-o
LParentClass;->fieldPrivateLightGreylist:I,greylist
+LParentClass;->fieldPrivateStaticBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
LParentClass;->fieldPrivateStaticBlacklist:I,blacklist
LParentClass;->fieldPrivateStaticDarkGreylist:I,greylist-max-o
LParentClass;->fieldPrivateStaticLightGreylist:I,greylist
+LParentClass;->fieldProtectedBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
LParentClass;->fieldProtectedBlacklist:I,blacklist
LParentClass;->fieldProtectedDarkGreylist:I,greylist-max-o
LParentClass;->fieldProtectedLightGreylist:I,greylist
+LParentClass;->fieldProtectedStaticBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
LParentClass;->fieldProtectedStaticBlacklist:I,blacklist
LParentClass;->fieldProtectedStaticDarkGreylist:I,greylist-max-o
LParentClass;->fieldProtectedStaticLightGreylist:I,greylist
+LParentClass;->fieldPublicBlacklistAndCorePlatformApiB:I,blacklist,core-platform-api
+LParentClass;->fieldPublicBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
LParentClass;->fieldPublicBlacklistB:I,blacklist
LParentClass;->fieldPublicBlacklist:I,blacklist
LParentClass;->fieldPublicDarkGreylistB:I,greylist-max-o
LParentClass;->fieldPublicDarkGreylist:I,greylist-max-o
LParentClass;->fieldPublicLightGreylistB:I,greylist
LParentClass;->fieldPublicLightGreylist:I,greylist
+LParentClass;->fieldPublicStaticBlacklistAndCorePlatformApiB:I,blacklist,core-platform-api
+LParentClass;->fieldPublicStaticBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
LParentClass;->fieldPublicStaticBlacklistB:I,blacklist
LParentClass;->fieldPublicStaticBlacklist:I,blacklist
LParentClass;->fieldPublicStaticDarkGreylistB:I,greylist-max-o
@@ -33,49 +44,65 @@
LParentClass;->fieldPublicStaticLightGreylist:I,greylist
LParentClass;-><init>(DB)V,greylist-max-o
LParentClass;-><init>(DC)V,blacklist
+LParentClass;-><init>(DI)V,blacklist,core-platform-api
LParentClass;-><init>(DZ)V,greylist
LParentClass;-><init>(FB)V,greylist-max-o
LParentClass;-><init>(FC)V,blacklist
+LParentClass;-><init>(FI)V,blacklist,core-platform-api
LParentClass;-><init>(FZ)V,greylist
LParentClass;-><init>(IB)V,greylist-max-o
LParentClass;-><init>(IC)V,blacklist
+LParentClass;-><init>(II)V,blacklist,core-platform-api
LParentClass;-><init>(IZ)V,greylist
LParentClass;-><init>(JB)V,greylist-max-o
LParentClass;-><init>(JC)V,blacklist
+LParentClass;-><init>(JI)V,blacklist,core-platform-api
LParentClass;-><init>(JZ)V,greylist
+LParentClass;->methodPackageBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentClass;->methodPackageBlacklist()I,blacklist
LParentClass;->methodPackageDarkGreylist()I,greylist-max-o
LParentClass;->methodPackageLightGreylist()I,greylist
+LParentClass;->methodPackageStaticBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentClass;->methodPackageStaticBlacklist()I,blacklist
LParentClass;->methodPackageStaticDarkGreylist()I,greylist-max-o
LParentClass;->methodPackageStaticLightGreylist()I,greylist
+LParentClass;->methodPrivateBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentClass;->methodPrivateBlacklist()I,blacklist
LParentClass;->methodPrivateDarkGreylist()I,greylist-max-o
LParentClass;->methodPrivateLightGreylist()I,greylist
+LParentClass;->methodPrivateStaticBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentClass;->methodPrivateStaticBlacklist()I,blacklist
LParentClass;->methodPrivateStaticDarkGreylist()I,greylist-max-o
LParentClass;->methodPrivateStaticLightGreylist()I,greylist
+LParentClass;->methodProtectedBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentClass;->methodProtectedBlacklist()I,blacklist
LParentClass;->methodProtectedDarkGreylist()I,greylist-max-o
LParentClass;->methodProtectedLightGreylist()I,greylist
+LParentClass;->methodProtectedStaticBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentClass;->methodProtectedStaticBlacklist()I,blacklist
LParentClass;->methodProtectedStaticDarkGreylist()I,greylist-max-o
LParentClass;->methodProtectedStaticLightGreylist()I,greylist
+LParentClass;->methodPublicBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentClass;->methodPublicBlacklist()I,blacklist
LParentClass;->methodPublicDarkGreylist()I,greylist-max-o
LParentClass;->methodPublicLightGreylist()I,greylist
+LParentClass;->methodPublicStaticBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentClass;->methodPublicStaticBlacklist()I,blacklist
LParentClass;->methodPublicStaticDarkGreylist()I,greylist-max-o
LParentClass;->methodPublicStaticLightGreylist()I,greylist
+LParentInterface;->fieldPublicStaticBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
LParentInterface;->fieldPublicStaticBlacklist:I,blacklist
LParentInterface;->fieldPublicStaticDarkGreylist:I,greylist-max-o
LParentInterface;->fieldPublicStaticLightGreylist:I,greylist
+LParentInterface;->methodPublicBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentInterface;->methodPublicBlacklist()I,blacklist
LParentInterface;->methodPublicDarkGreylist()I,greylist-max-o
+LParentInterface;->methodPublicDefaultBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentInterface;->methodPublicDefaultBlacklist()I,blacklist
LParentInterface;->methodPublicDefaultDarkGreylist()I,greylist-max-o
LParentInterface;->methodPublicDefaultLightGreylist()I,greylist
LParentInterface;->methodPublicLightGreylist()I,greylist
+LParentInterface;->methodPublicStaticBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentInterface;->methodPublicStaticBlacklist()I,blacklist
LParentInterface;->methodPublicStaticDarkGreylist()I,greylist-max-o
LParentInterface;->methodPublicStaticLightGreylist()I,greylist
diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc
index 2464263..8dfb402 100644
--- a/test/674-hiddenapi/hiddenapi.cc
+++ b/test/674-hiddenapi/hiddenapi.cc
@@ -27,40 +27,59 @@
namespace art {
namespace Test674HiddenApi {
+std::vector<std::vector<std::unique_ptr<const DexFile>>> opened_dex_files;
+
extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) {
Runtime* runtime = Runtime::Current();
runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+ runtime->SetCorePlatformApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
runtime->SetTargetSdkVersion(
static_cast<uint32_t>(hiddenapi::ApiList::GreylistMaxO().GetMaxAllowedSdkVersion()));
runtime->SetDedupeHiddenApiWarnings(false);
}
-extern "C" JNIEXPORT void JNICALL Java_Main_appendToBootClassLoader(
- JNIEnv* env, jclass, jstring jpath) {
+extern "C" JNIEXPORT void JNICALL Java_Main_setDexDomain(
+ JNIEnv*, jclass, jint int_index, jboolean is_core_platform) {
+ size_t index = static_cast<size_t>(int_index);
+ CHECK_LT(index, opened_dex_files.size());
+ for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files[index]) {
+ const_cast<DexFile*>(dex_file.get())->SetHiddenapiDomain(
+ (is_core_platform == JNI_FALSE) ? hiddenapi::Domain::kPlatform
+ : hiddenapi::Domain::kCorePlatform);
+ }
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_appendToBootClassLoader(
+ JNIEnv* env, jclass klass, jstring jpath, jboolean is_core_platform) {
ScopedUtfChars utf(env, jpath);
const char* path = utf.c_str();
- if (path == nullptr) {
- return;
- }
+ CHECK(path != nullptr);
+
+ const size_t index = opened_dex_files.size();
+ const jint int_index = static_cast<jint>(index);
+ opened_dex_files.push_back(std::vector<std::unique_ptr<const DexFile>>());
ArtDexFileLoader dex_loader;
std::string error_msg;
- std::vector<std::unique_ptr<const DexFile>> dex_files;
+
if (!dex_loader.Open(path,
path,
/* verify */ false,
/* verify_checksum */ true,
&error_msg,
- &dex_files)) {
+ &opened_dex_files[index])) {
LOG(FATAL) << "Could not open " << path << " for boot classpath extension: " << error_msg;
UNREACHABLE();
}
+ Java_Main_setDexDomain(env, klass, int_index, is_core_platform);
+
ScopedObjectAccess soa(Thread::Current());
- for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
- Runtime::Current()->GetClassLinker()->AppendToBootClassPath(
- Thread::Current(), *dex_file.release());
+ for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files[index]) {
+ Runtime::Current()->GetClassLinker()->AppendToBootClassPath(Thread::Current(), *dex_file.get());
}
+
+ return int_index;
}
static jobject NewInstance(JNIEnv* env, jclass klass) {
diff --git a/test/674-hiddenapi/run b/test/674-hiddenapi/run
new file mode 100755
index 0000000..2babeef
--- /dev/null
+++ b/test/674-hiddenapi/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Make verification soft fail so that we can re-verify boot classpath
+# methods at runtime.
+exec ${RUN} $@ --verify-soft-fail
\ No newline at end of file
diff --git a/test/674-hiddenapi/src-art/Main.java b/test/674-hiddenapi/src-art/Main.java
index 782748c..190f4ac 100644
--- a/test/674-hiddenapi/src-art/Main.java
+++ b/test/674-hiddenapi/src-art/Main.java
@@ -28,6 +28,13 @@
import java.util.zip.ZipFile;
public class Main {
+ // This needs to be kept in sync with DexDomain in ChildClass.
+ enum DexDomain {
+ CorePlatform,
+ Platform,
+ Application
+ }
+
public static void main(String[] args) throws Exception {
System.loadLibrary(args[0]);
prepareNativeLibFileName(args[0]);
@@ -40,70 +47,88 @@
// or test the wrong thing. We rely on not deduping hidden API warnings
// here for the same reasons), meaning the code under test and production
// code are running in different configurations. Each test should be run in
- // a fresh process to ensure that they are working correcting and not
- // accidentally interfering with eachother.
+ // a fresh process to ensure that they are working correctly and not
+ // accidentally interfering with each other.
+ // As a side effect, we also cannot test Platform->Platform and later
+ // Platform->CorePlatform as the former succeeds in verifying linkage usages
+ // that should fail in the latter.
// Run test with both parent and child dex files loaded with class loaders.
// The expectation is that hidden members in parent should be visible to
// the child.
- doTest(false, false, false);
+ doTest(DexDomain.Application, DexDomain.Application, false);
doUnloading();
// Now append parent dex file to boot class path and run again. This time
// the child dex file should not be able to access private APIs of the
// parent.
- appendToBootClassLoader(DEX_PARENT_BOOT);
- doTest(true, false, false);
+ int parentIdx = appendToBootClassLoader(DEX_PARENT_BOOT, /* isCorePlatform */ false);
+ doTest(DexDomain.Platform, DexDomain.Application, false);
doUnloading();
// Now run the same test again, but with the blacklist exmemptions list set
// to "L" which matches everything.
- doTest(true, false, true);
+ doTest(DexDomain.Platform, DexDomain.Application, true);
doUnloading();
- // And finally append to child to boot class path as well. With both in the
- // boot class path, access should be granted.
- appendToBootClassLoader(DEX_CHILD);
- doTest(true, true, false);
+ // Repeat the two tests above, only with parent being a core-platform dex file.
+ setDexDomain(parentIdx, /* isCorePlatform */ true);
+ doTest(DexDomain.CorePlatform, DexDomain.Application, false);
+ doUnloading();
+ doTest(DexDomain.CorePlatform, DexDomain.Application, true);
+ doUnloading();
+
+ // Append child to boot class path, first as a platform dex file.
+ // It should not be allowed to access non-public, non-core platform API members.
+ int childIdx = appendToBootClassLoader(DEX_CHILD, /* isCorePlatform */ false);
+ doTest(DexDomain.CorePlatform, DexDomain.Platform, false);
+ doUnloading();
+
+ // And finally change child to core-platform dex. With both in the boot classpath
+ // and both core-platform, access should be granted.
+ setDexDomain(childIdx, /* isCorePlatform */ true);
+ doTest(DexDomain.CorePlatform, DexDomain.CorePlatform, false);
doUnloading();
}
- private static void doTest(boolean parentInBoot, boolean childInBoot, boolean whitelistAllApis)
- throws Exception {
+ private static void doTest(DexDomain parentDomain, DexDomain childDomain,
+ boolean whitelistAllApis) throws Exception {
// Load parent dex if it is not in boot class path.
ClassLoader parentLoader = null;
- if (parentInBoot) {
- parentLoader = BOOT_CLASS_LOADER;
- } else {
+ if (parentDomain == DexDomain.Application) {
parentLoader = new PathClassLoader(DEX_PARENT, ClassLoader.getSystemClassLoader());
+ } else {
+ parentLoader = BOOT_CLASS_LOADER;
}
// Load child dex if it is not in boot class path.
ClassLoader childLoader = null;
- if (childInBoot) {
+ if (childDomain == DexDomain.Application) {
+ childLoader = new InMemoryDexClassLoader(readDexFile(DEX_CHILD), parentLoader);
+ } else {
if (parentLoader != BOOT_CLASS_LOADER) {
throw new IllegalStateException(
"DeclaringClass must be in parent class loader of CallingClass");
}
childLoader = BOOT_CLASS_LOADER;
- } else {
- childLoader = new InMemoryDexClassLoader(readDexFile(DEX_CHILD), parentLoader);
}
// Create a unique copy of the native library. Each shared library can only
// be loaded once, but for some reason even classes from a class loader
// cannot register their native methods against symbols in a shared library
// loaded by their parent class loader.
- String nativeLibCopy = createNativeLibCopy(parentInBoot, childInBoot, whitelistAllApis);
+ String nativeLibCopy = createNativeLibCopy(parentDomain, childDomain, whitelistAllApis);
if (whitelistAllApis) {
VMRuntime.getRuntime().setHiddenApiExemptions(new String[]{"L"});
}
// Invoke ChildClass.runTest
- Class.forName("ChildClass", true, childLoader)
- .getDeclaredMethod("runTest", String.class, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE)
- .invoke(null, nativeLibCopy, parentInBoot, childInBoot, whitelistAllApis);
+ Class<?> childClass = Class.forName("ChildClass", true, childLoader);
+ Method runTestMethod = childClass.getDeclaredMethod(
+ "runTest", String.class, Integer.TYPE, Integer.TYPE, Boolean.TYPE);
+ runTestMethod.invoke(null, nativeLibCopy, parentDomain.ordinal(), childDomain.ordinal(),
+ whitelistAllApis);
VMRuntime.getRuntime().setHiddenApiExemptions(new String[0]);
}
@@ -146,11 +171,11 @@
// Copy native library to a new file with a unique name so it does not
// conflict with other loaded instance of the same binary file.
- private static String createNativeLibCopy(
- boolean parentInBoot, boolean childInBoot, boolean whitelistAllApis) throws Exception {
+ private static String createNativeLibCopy(DexDomain parentDomain, DexDomain childDomain,
+ boolean whitelistAllApis) throws Exception {
String tempFileName = System.mapLibraryName(
- "hiddenapitest_" + (parentInBoot ? "1" : "0") + (childInBoot ? "1" : "0") +
- (whitelistAllApis ? "1" : "0"));
+ "hiddenapitest_" + (parentDomain.ordinal()) + (childDomain.ordinal()) +
+ (whitelistAllApis ? "1" : "0"));
File tempFile = new File(System.getenv("DEX_LOCATION"), tempFileName);
Files.copy(new File(nativeLibFileName).toPath(), tempFile.toPath());
return tempFile.getAbsolutePath();
@@ -175,6 +200,7 @@
private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader();
- private static native void appendToBootClassLoader(String dexPath);
+ private static native int appendToBootClassLoader(String dexPath, boolean isCorePlatform);
+ private static native void setDexDomain(int index, boolean isCorePlatform);
private static native void init();
}
diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java
index 3427b8e..f120bda 100644
--- a/test/674-hiddenapi/src-ex/ChildClass.java
+++ b/test/674-hiddenapi/src-ex/ChildClass.java
@@ -45,7 +45,8 @@
Whitelist(PrimitiveType.TShort),
LightGreylist(PrimitiveType.TBoolean),
DarkGreylist(PrimitiveType.TByte),
- Blacklist(PrimitiveType.TCharacter);
+ Blacklist(PrimitiveType.TCharacter),
+ BlacklistAndCorePlatformApi(PrimitiveType.TInteger);
Hiddenness(PrimitiveType type) { mAssociatedType = type; }
public PrimitiveType mAssociatedType;
@@ -67,19 +68,34 @@
Denied,
}
+ // This needs to be kept in sync with DexDomain in Main.
+ enum DexDomain {
+ CorePlatform,
+ Platform,
+ Application
+ }
+
private static final boolean booleanValues[] = new boolean[] { false, true };
- public static void runTest(String libFileName, boolean expectedParentInBoot,
- boolean expectedChildInBoot, boolean everythingWhitelisted) throws Exception {
+ public static void runTest(String libFileName, int parentDomainOrdinal,
+ int childDomainOrdinal, boolean everythingWhitelisted) throws Exception {
System.load(libFileName);
+ parentDomain = DexDomain.values()[parentDomainOrdinal];
+ childDomain = DexDomain.values()[childDomainOrdinal];
+
+ configMessage = "parentDomain=" + parentDomain.name() + ", childDomain=" + childDomain.name()
+ + ", everythingWhitelisted=" + everythingWhitelisted;
+
// Check expectations about loading into boot class path.
- isParentInBoot = (ParentClass.class.getClassLoader().getParent() == null);
+ boolean isParentInBoot = (ParentClass.class.getClassLoader().getParent() == null);
+ boolean expectedParentInBoot = (parentDomain != DexDomain.Application);
if (isParentInBoot != expectedParentInBoot) {
throw new RuntimeException("Expected ParentClass " +
(expectedParentInBoot ? "" : "not ") + "in boot class path");
}
- isChildInBoot = (ChildClass.class.getClassLoader().getParent() == null);
+ boolean isChildInBoot = (ChildClass.class.getClassLoader().getParent() == null);
+ boolean expectedChildInBoot = (childDomain != DexDomain.Application);
if (isChildInBoot != expectedChildInBoot) {
throw new RuntimeException("Expected ChildClass " + (expectedChildInBoot ? "" : "not ") +
"in boot class path");
@@ -92,14 +108,26 @@
// Run meaningful combinations of access flags.
for (Hiddenness hiddenness : Hiddenness.values()) {
final Behaviour expected;
+ final boolean invokesMemberCallback;
// Warnings are now disabled whenever access is granted, even for
// greylisted APIs. This is the behaviour for release builds.
- if (isSameBoot || everythingWhitelisted || hiddenness == Hiddenness.Whitelist) {
+ if (everythingWhitelisted || hiddenness == Hiddenness.Whitelist) {
expected = Behaviour.Granted;
- } else if (hiddenness == Hiddenness.Blacklist) {
+ invokesMemberCallback = false;
+ } else if (parentDomain == DexDomain.CorePlatform && childDomain == DexDomain.Platform) {
+ expected = (hiddenness == Hiddenness.BlacklistAndCorePlatformApi)
+ ? Behaviour.Granted : Behaviour.Denied;
+ invokesMemberCallback = false;
+ } else if (isSameBoot) {
+ expected = Behaviour.Granted;
+ invokesMemberCallback = false;
+ } else if (hiddenness == Hiddenness.Blacklist ||
+ hiddenness == Hiddenness.BlacklistAndCorePlatformApi) {
expected = Behaviour.Denied;
+ invokesMemberCallback = true;
} else {
expected = Behaviour.Warning;
+ invokesMemberCallback = true;
}
for (boolean isStatic : booleanValues) {
@@ -109,8 +137,10 @@
// Test reflection and JNI on methods and fields
for (Class klass : new Class<?>[] { ParentClass.class, ParentInterface.class }) {
String baseName = visibility.name() + suffix;
- checkField(klass, "field" + baseName, isStatic, visibility, expected);
- checkMethod(klass, "method" + baseName, isStatic, visibility, expected);
+ checkField(klass, "field" + baseName, isStatic, visibility, expected,
+ invokesMemberCallback);
+ checkMethod(klass, "method" + baseName, isStatic, visibility, expected,
+ invokesMemberCallback);
}
// Check whether one can use a class constructor.
@@ -118,7 +148,8 @@
// Check whether one can use an interface default method.
String name = "method" + visibility.name() + "Default" + hiddenness.name();
- checkMethod(ParentInterface.class, name, /*isStatic*/ false, visibility, expected);
+ checkMethod(ParentInterface.class, name, /*isStatic*/ false, visibility, expected,
+ invokesMemberCallback);
}
// Test whether static linking succeeds.
@@ -181,11 +212,10 @@
}
private static void checkField(Class<?> klass, String name, boolean isStatic,
- Visibility visibility, Behaviour behaviour) throws Exception {
+ Visibility visibility, Behaviour behaviour, boolean invokesMemberCallback) throws Exception {
boolean isPublic = (visibility == Visibility.Public);
boolean canDiscover = (behaviour != Behaviour.Denied);
- boolean invokesMemberCallback = (behaviour != Behaviour.Granted);
if (klass.isInterface() && (!isStatic || !isPublic)) {
// Interfaces only have public static fields.
@@ -275,7 +305,7 @@
}
private static void checkMethod(Class<?> klass, String name, boolean isStatic,
- Visibility visibility, Behaviour behaviour) throws Exception {
+ Visibility visibility, Behaviour behaviour, boolean invokesMemberCallback) throws Exception {
boolean isPublic = (visibility == Visibility.Public);
if (klass.isInterface() && !isPublic) {
@@ -284,7 +314,6 @@
}
boolean canDiscover = (behaviour != Behaviour.Denied);
- boolean invokesMemberCallback = (behaviour != Behaviour.Granted);
// Test discovery with reflection.
@@ -428,8 +457,7 @@
if (Reflection.canUseNewInstance(klass) != canAccess) {
throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
- "be able to construct " + klass.getName() + ". " +
- "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+ "be able to construct " + klass.getName() + ". " + configMessage);
}
}
@@ -439,8 +467,7 @@
if (Linking.canAccess(className, takesParameter) != canAccess) {
throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
- "be able to verify " + className + "." +
- "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+ "be able to verify " + className + "." + configMessage);
}
}
@@ -448,16 +475,13 @@
String fn, boolean canAccess) {
throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() +
"." + name + " to " + (canAccess ? "" : "not ") + "be discoverable with " + fn + ". " +
- "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " +
- "everythingWhitelisted = " + everythingWhitelisted);
+ configMessage);
}
private static void throwAccessException(Class<?> klass, String name, boolean isField,
String fn) {
throw new RuntimeException("Expected to be able to access " + (isField ? "field " : "method ") +
- klass.getName() + "." + name + " using " + fn + ". " +
- "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " +
- "everythingWhitelisted = " + everythingWhitelisted);
+ klass.getName() + "." + name + " using " + fn + ". " + configMessage);
}
private static void throwModifiersException(Class<?> klass, String name, boolean isField) {
@@ -465,7 +489,9 @@
"." + name + " to not expose hidden modifiers");
}
- private static boolean isParentInBoot;
- private static boolean isChildInBoot;
+ private static DexDomain parentDomain;
+ private static DexDomain childDomain;
private static boolean everythingWhitelisted;
+
+ private static String configMessage;
}
diff --git a/test/674-hiddenapi/src-ex/Linking.java b/test/674-hiddenapi/src-ex/Linking.java
index 0fa0b19..5aa3663 100644
--- a/test/674-hiddenapi/src-ex/Linking.java
+++ b/test/674-hiddenapi/src-ex/Linking.java
@@ -62,6 +62,12 @@
}
}
+class LinkFieldGetBlacklistAndCorePlatformApi {
+ public static int access() {
+ return new ParentClass().fieldPublicBlacklistAndCorePlatformApi;
+ }
+}
+
// INSTANCE FIELD SET
class LinkFieldSetWhitelist {
@@ -92,6 +98,13 @@
}
}
+class LinkFieldSetBlacklistAndCorePlatformApi {
+ public static void access(int x) {
+ // Need to use a different field from the getter to bypass DexCache.
+ new ParentClass().fieldPublicBlacklistAndCorePlatformApiB = x;
+ }
+}
+
// STATIC FIELD GET
class LinkFieldGetStaticWhitelist {
@@ -118,6 +131,12 @@
}
}
+class LinkFieldGetStaticBlacklistAndCorePlatformApi {
+ public static int access() {
+ return ParentClass.fieldPublicStaticBlacklistAndCorePlatformApi;
+ }
+}
+
// STATIC FIELD SET
class LinkFieldSetStaticWhitelist {
@@ -148,6 +167,13 @@
}
}
+class LinkFieldSetStaticBlacklistAndCorePlatformApi {
+ public static void access(int x) {
+ // Need to use a different field from the getter to bypass DexCache.
+ ParentClass.fieldPublicStaticBlacklistAndCorePlatformApiB = x;
+ }
+}
+
// INVOKE INSTANCE METHOD
class LinkMethodWhitelist {
@@ -174,6 +200,12 @@
}
}
+class LinkMethodBlacklistAndCorePlatformApi {
+ public static int access() {
+ return new ParentClass().methodPublicBlacklistAndCorePlatformApi();
+ }
+}
+
// INVOKE INSTANCE INTERFACE METHOD
class LinkMethodInterfaceWhitelist {
@@ -200,6 +232,12 @@
}
}
+class LinkMethodInterfaceBlacklistAndCorePlatformApi {
+ public static int access() {
+ return DummyClass.getInterfaceInstance().methodPublicBlacklistAndCorePlatformApi();
+ }
+}
+
// INVOKE STATIC METHOD
class LinkMethodStaticWhitelist {
@@ -226,6 +264,12 @@
}
}
+class LinkMethodStaticBlacklistAndCorePlatformApi {
+ public static int access() {
+ return ParentClass.methodPublicStaticBlacklistAndCorePlatformApi();
+ }
+}
+
// INVOKE INTERFACE STATIC METHOD
class LinkMethodInterfaceStaticWhitelist {
@@ -251,3 +295,9 @@
return ParentInterface.methodPublicStaticBlacklist();
}
}
+
+class LinkMethodInterfaceStaticBlacklistAndCorePlatformApi {
+ public static int access() {
+ return ParentInterface.methodPublicStaticBlacklistAndCorePlatformApi();
+ }
+}
diff --git a/test/674-hiddenapi/src/DummyClass.java b/test/674-hiddenapi/src/DummyClass.java
index 51281a2..afba747 100644
--- a/test/674-hiddenapi/src/DummyClass.java
+++ b/test/674-hiddenapi/src/DummyClass.java
@@ -19,6 +19,7 @@
public int methodPublicLightGreylist() { return 2; }
public int methodPublicDarkGreylist() { return 3; }
public int methodPublicBlacklist() { return 4; }
+ public int methodPublicBlacklistAndCorePlatformApi() { return 5; }
public static ParentInterface getInterfaceInstance() {
return new DummyClass();
diff --git a/test/674-hiddenapi/src/NullaryConstructorBlacklistAndCorePlatformApi.java b/test/674-hiddenapi/src/NullaryConstructorBlacklistAndCorePlatformApi.java
new file mode 100644
index 0000000..86af29e
--- /dev/null
+++ b/test/674-hiddenapi/src/NullaryConstructorBlacklistAndCorePlatformApi.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class NullaryConstructorBlacklistAndCorePlatformApi {
+ public NullaryConstructorBlacklistAndCorePlatformApi() { x = 22; }
+ public NullaryConstructorBlacklistAndCorePlatformApi(int y) { x = y; }
+ protected int x;
+}
diff --git a/test/674-hiddenapi/src/ParentClass.java b/test/674-hiddenapi/src/ParentClass.java
index 07e84cc..1442392 100644
--- a/test/674-hiddenapi/src/ParentClass.java
+++ b/test/674-hiddenapi/src/ParentClass.java
@@ -43,6 +43,12 @@
private int fieldPrivateBlacklist = 244;
public int fieldPublicBlacklistB = 245;
+ public int fieldPublicBlacklistAndCorePlatformApi = 251;
+ int fieldPackageBlacklistAndCorePlatformApi = 252;
+ protected int fieldProtectedBlacklistAndCorePlatformApi = 253;
+ private int fieldPrivateBlacklistAndCorePlatformApi = 254;
+ public int fieldPublicBlacklistAndCorePlatformApiB = 255;
+
// STATIC FIELD
public static int fieldPublicStaticWhitelist = 111;
@@ -69,6 +75,12 @@
private static int fieldPrivateStaticBlacklist = 144;
public static int fieldPublicStaticBlacklistB = 145;
+ public static int fieldPublicStaticBlacklistAndCorePlatformApi = 151;
+ static int fieldPackageStaticBlacklistAndCorePlatformApi = 152;
+ protected static int fieldProtectedStaticBlacklistAndCorePlatformApi = 153;
+ private static int fieldPrivateStaticBlacklistAndCorePlatformApi = 154;
+ public static int fieldPublicStaticBlacklistAndCorePlatformApiB = 155;
+
// INSTANCE METHOD
public int methodPublicWhitelist() { return 411; }
@@ -91,6 +103,11 @@
protected int methodProtectedBlacklist() { return 443; }
private int methodPrivateBlacklist() { return 444; }
+ public int methodPublicBlacklistAndCorePlatformApi() { return 451; }
+ int methodPackageBlacklistAndCorePlatformApi() { return 452; }
+ protected int methodProtectedBlacklistAndCorePlatformApi() { return 453; }
+ private int methodPrivateBlacklistAndCorePlatformApi() { return 454; }
+
// STATIC METHOD
public static int methodPublicStaticWhitelist() { return 311; }
@@ -113,6 +130,11 @@
protected static int methodProtectedStaticBlacklist() { return 343; }
private static int methodPrivateStaticBlacklist() { return 344; }
+ public static int methodPublicStaticBlacklistAndCorePlatformApi() { return 351; }
+ static int methodPackageStaticBlacklistAndCorePlatformApi() { return 352; }
+ protected static int methodProtectedStaticBlacklistAndCorePlatformApi() { return 353; }
+ private static int methodPrivateStaticBlacklistAndCorePlatformApi() { return 354; }
+
// CONSTRUCTOR
// Whitelist
@@ -139,6 +161,12 @@
protected ParentClass(long x, char y) {}
private ParentClass(double x, char y) {}
+ // Blacklist and CorePlatformApi
+ public ParentClass(int x, int y) {}
+ ParentClass(float x, int y) {}
+ protected ParentClass(long x, int y) {}
+ private ParentClass(double x, int y) {}
+
// HELPERS
public int callMethodPublicWhitelist() { return methodPublicWhitelist(); }
@@ -157,4 +185,15 @@
public int callMethodPackageBlacklist() { return methodPackageBlacklist(); }
public int callMethodProtectedBlacklist() { return methodProtectedBlacklist(); }
+ public int callMethodPublicBlacklistAndCorePlatformApi() {
+ return methodPublicBlacklistAndCorePlatformApi();
+ }
+
+ public int callMethodPackageBlacklistAndCorePlatformApi() {
+ return methodPackageBlacklistAndCorePlatformApi();
+ }
+
+ public int callMethodProtectedBlacklistAndCorePlatformApi() {
+ return methodProtectedBlacklistAndCorePlatformApi();
+ }
}
diff --git a/test/674-hiddenapi/src/ParentInterface.java b/test/674-hiddenapi/src/ParentInterface.java
index f79ac9d..1c5b58f 100644
--- a/test/674-hiddenapi/src/ParentInterface.java
+++ b/test/674-hiddenapi/src/ParentInterface.java
@@ -20,22 +20,26 @@
static int fieldPublicStaticLightGreylist = 12;
static int fieldPublicStaticDarkGreylist = 13;
static int fieldPublicStaticBlacklist = 14;
+ static int fieldPublicStaticBlacklistAndCorePlatformApi = 15;
// INSTANCE METHOD
int methodPublicWhitelist();
int methodPublicLightGreylist();
int methodPublicDarkGreylist();
int methodPublicBlacklist();
+ int methodPublicBlacklistAndCorePlatformApi();
// STATIC METHOD
static int methodPublicStaticWhitelist() { return 21; }
static int methodPublicStaticLightGreylist() { return 22; }
static int methodPublicStaticDarkGreylist() { return 23; }
static int methodPublicStaticBlacklist() { return 24; }
+ static int methodPublicStaticBlacklistAndCorePlatformApi() { return 25; }
// DEFAULT METHOD
default int methodPublicDefaultWhitelist() { return 31; }
default int methodPublicDefaultLightGreylist() { return 32; }
default int methodPublicDefaultDarkGreylist() { return 33; }
default int methodPublicDefaultBlacklist() { return 34; }
+ default int methodPublicDefaultBlacklistAndCorePlatformApi() { return 35; }
}
diff --git a/test/999-redefine-hiddenapi/src/Main.java b/test/999-redefine-hiddenapi/src/Main.java
index 3d9bbda..014ea16 100644
--- a/test/999-redefine-hiddenapi/src/Main.java
+++ b/test/999-redefine-hiddenapi/src/Main.java
@@ -27,7 +27,7 @@
init();
// Load the '-ex' APK and attach it to the boot class path.
- appendToBootClassLoader(DEX_EXTRA);
+ appendToBootClassLoader(DEX_EXTRA, /* isCorePlatform */ false);
// Find the test class in boot class loader and verify that its members are hidden.
Class<?> klass = Class.forName("art.Test999", true, BOOT_CLASS_LOADER);
@@ -67,7 +67,7 @@
private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader();
// Native functions. Note that these are implemented in 674-hiddenapi/hiddenapi.cc.
- private static native void appendToBootClassLoader(String dexPath);
+ private static native void appendToBootClassLoader(String dexPath, boolean isCorePlatform);
private static native void init();
/**
diff --git a/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java b/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
index 1da848b..9c4e57e 100644
--- a/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
+++ b/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
@@ -17,6 +17,7 @@
package com.android.class2greylist;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.ImmutableSet;
@@ -34,10 +35,12 @@
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* Build time tool for extracting a list of members from jar files that have the @UsedByApps
@@ -67,7 +70,6 @@
}
private final Status mStatus;
- private final String mPublicApiListFile;
private final String mCsvFlagsFile;
private final String mCsvMetadataFile;
private final String[] mJarFiles;
@@ -77,10 +79,11 @@
public static void main(String[] args) {
Options options = new Options();
options.addOption(OptionBuilder
- .withLongOpt("public-api-list")
+ .withLongOpt("stub-api-flags")
.hasArgs(1)
- .withDescription("Public API list file. Used to de-dupe bridge methods.")
- .create("p"));
+ .withDescription("CSV file with API flags generated from public API stubs. " +
+ "Used to de-dupe bridge methods.")
+ .create("s"));
options.addOption(OptionBuilder
.withLongOpt("write-flags-csv")
.hasArgs(1)
@@ -139,7 +142,7 @@
try {
Class2Greylist c2gl = new Class2Greylist(
status,
- cmd.getOptionValue('p', null),
+ cmd.getOptionValue('s', null),
cmd.getOptionValue('w', null),
cmd.getOptionValue('c', null),
jarFiles);
@@ -158,11 +161,10 @@
}
@VisibleForTesting
- Class2Greylist(Status status, String publicApiListFile, String csvFlagsFile,
+ Class2Greylist(Status status, String stubApiFlagsFile, String csvFlagsFile,
String csvMetadataFile, String[] jarFiles)
throws IOException {
mStatus = status;
- mPublicApiListFile = publicApiListFile;
mCsvFlagsFile = csvFlagsFile;
mCsvMetadataFile = csvMetadataFile;
mJarFiles = jarFiles;
@@ -172,9 +174,13 @@
mOutput = new HiddenapiFlagsWriter(mCsvFlagsFile);
}
- if (mPublicApiListFile != null) {
- mPublicApis = Sets.newHashSet(
- Files.readLines(new File(mPublicApiListFile), Charset.forName("UTF-8")));
+ if (stubApiFlagsFile != null) {
+ mPublicApis =
+ Files.readLines(new File(stubApiFlagsFile), Charset.forName("UTF-8")).stream()
+ .map(s -> Splitter.on(",").splitToList(s))
+ .filter(s -> s.contains(FLAG_WHITELIST))
+ .map(s -> s.get(0))
+ .collect(Collectors.toSet());
} else {
mPublicApis = Collections.emptySet();
}
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
index fdb0d8e..97fbcbf 100644
--- a/tools/hiddenapi/hiddenapi.cc
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -33,6 +33,7 @@
#include "dex/dex_file-inl.h"
namespace art {
+namespace hiddenapi {
const char kErrorHelp[] = "\nSee go/hiddenapi-error for help.";
@@ -83,12 +84,12 @@
UsageError("");
UsageError(" Command \"list\": dump lists of public and private API");
UsageError(" --boot-dex=<filename>: dex file which belongs to boot class path");
- UsageError(" --stub-classpath=<filenames>: colon-separated list of dex/apk files");
- UsageError(" which form API stubs of boot class path. Multiple classpaths can");
- UsageError(" be specified");
+ 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("");
- UsageError(" --out-public=<filename>: output file for a list of all public APIs");
- UsageError(" --out-private=<filename>: output file for a list of all private APIs");
+ UsageError(" --out-api-flags=<filename>: output file for a CSV file with API flags");
UsageError("");
exit(EXIT_FAILURE);
@@ -619,10 +620,10 @@
// 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(hiddenapi::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.
@@ -910,7 +911,7 @@
} else if (option.starts_with("--output-dex=")) {
output_dex_paths_.push_back(option.substr(strlen("--output-dex=")).ToString());
} else if (option.starts_with("--api-flags=")) {
- api_list_path_ = option.substr(strlen("--api-flags=")).ToString();
+ api_flags_path_ = option.substr(strlen("--api-flags=")).ToString();
} else if (option == "--no-force-assign-all") {
force_assign_all_ = false;
} else {
@@ -923,13 +924,16 @@
const StringPiece option(argv[i]);
if (option.starts_with("--boot-dex=")) {
boot_dex_paths_.push_back(option.substr(strlen("--boot-dex=")).ToString());
- } else if (option.starts_with("--stub-classpath=")) {
- stub_classpaths_.push_back(android::base::Split(
- option.substr(strlen("--stub-classpath=")).ToString(), ":"));
- } else if (option.starts_with("--out-public=")) {
- out_public_path_ = option.substr(strlen("--out-public=")).ToString();
- } else if (option.starts_with("--out-private=")) {
- out_private_path_ = option.substr(strlen("--out-private=")).ToString();
+ } else if (option.starts_with("--public-stub-classpath=")) {
+ 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 {
Usage("Unknown argument '%s'", option.data());
}
@@ -951,7 +955,7 @@
}
// Load dex signatures.
- std::map<std::string, hiddenapi::ApiList> api_list = OpenApiFile(api_list_path_);
+ std::map<std::string, ApiList> api_list = OpenApiFile(api_flags_path_);
// Iterate over input dex files and insert HiddenapiClassData sections.
for (size_t i = 0; i < boot_dex_paths_.size(); ++i) {
@@ -975,7 +979,7 @@
LOG(WARNING) << "Could not find hiddenapi flags for dex entry: "
<< boot_member.GetApiEntry();
}
- builder.WriteFlags(api_list_found ? it->second : hiddenapi::ApiList::Whitelist());
+ builder.WriteFlags(api_list_found ? it->second : ApiList::Whitelist());
};
auto fn_field = [&](const ClassAccessor::Field& boot_field) {
fn_shared(DexMember(boot_class, boot_field));
@@ -994,34 +998,29 @@
}
}
- std::map<std::string, hiddenapi::ApiList> OpenApiFile(const std::string& path) {
+ std::map<std::string, ApiList> OpenApiFile(const std::string& path) {
CHECK(!path.empty());
std::ifstream api_file(path, std::ifstream::in);
CHECK(!api_file.fail()) << "Unable to open file '" << path << "' " << strerror(errno);
- std::map<std::string, hiddenapi::ApiList> api_flag_map;
+ 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;
+ CHECK_GT(values.size(), 1u) << path << ":" << line_number
+ << ": No flags found: " << line << kErrorHelp;
+
const std::string& signature = values[0];
+ CHECK(api_flag_map.find(signature) == api_flag_map.end()) << path << ":" << line_number
+ << ": Duplicate entry: " << signature << kErrorHelp;
- CHECK(api_flag_map.find(signature) == api_flag_map.end()) << "Duplicate entry in " << path
- << ": " << signature << 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& 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);
}
@@ -1035,15 +1034,13 @@
Usage("No boot DEX files specified");
} else if (stub_classpaths_.empty()) {
Usage("No stub DEX files specified");
- } else if (out_public_path_.empty()) {
- Usage("No public API output path specified");
- } else if (out_private_path_.empty()) {
- Usage("No private API output path specified");
+ } else if (api_flags_path_.empty()) {
+ Usage("No output path specified");
}
// Complete list of boot class path members. The associated boolean states
// whether it is public (true) or private (false).
- std::map<std::string, bool> boot_members;
+ std::map<std::string, ApiList> boot_members;
// Deduplicate errors before printing them.
std::set<std::string> unresolved;
@@ -1053,31 +1050,33 @@
Hierarchy boot_hierarchy(boot_classpath);
// Mark all boot dex members private.
- boot_classpath.ForEachDexMember([&boot_members](const DexMember& boot_member) {
- boot_members[boot_member.GetApiEntry()] = false;
+ boot_classpath.ForEachDexMember([&](const DexMember& boot_member) {
+ boot_members[boot_member.GetApiEntry()] = ApiList();
});
// Resolve each SDK dex member against the framework and mark it white.
- for (const std::vector<std::string>& stub_classpath_dex : stub_classpaths_) {
- ClassPath stub_classpath(stub_classpath_dex, /* open_writable= */ false);
+ for (const auto& cp_entry : stub_classpaths_) {
+ ClassPath stub_classpath(android::base::Split(cp_entry.first, ":"),
+ /* open_writable= */ false);
Hierarchy stub_hierarchy(stub_classpath);
+ const ApiList stub_api_list = cp_entry.second;
+
stub_classpath.ForEachDexMember(
- [&stub_hierarchy, &boot_hierarchy, &boot_members, &unresolved](
- const DexMember& stub_member) {
+ [&](const DexMember& stub_member) {
if (!stub_hierarchy.IsMemberVisible(stub_member)) {
// Typically fake constructors and inner-class `this` fields.
return;
}
bool resolved = boot_hierarchy.ForEachResolvableMember(
stub_member,
- [&boot_members](const DexMember& boot_member) {
+ [&](const DexMember& boot_member) {
std::string entry = boot_member.GetApiEntry();
auto it = boot_members.find(entry);
CHECK(it != boot_members.end());
- if (it->second) {
+ if (it->second.Contains(stub_api_list)) {
return false; // has been marked before
} else {
- it->second = true;
+ it->second |= stub_api_list;
return true; // marked for the first time
}
});
@@ -1093,17 +1092,15 @@
}
// Write into public/private API files.
- std::ofstream file_public(out_public_path_.c_str());
- std::ofstream file_private(out_private_path_.c_str());
- for (const std::pair<const std::string, bool>& entry : boot_members) {
- if (entry.second) {
- file_public << entry.first << std::endl;
+ std::ofstream file_flags(api_flags_path_.c_str());
+ for (const auto& entry : boot_members) {
+ if (entry.second.IsEmpty()) {
+ file_flags << entry.first << std::endl;
} else {
- file_private << entry.first << std::endl;
+ file_flags << entry.first << "," << entry.second << std::endl;
}
}
- file_public.close();
- file_private.close();
+ file_flags.close();
}
// Whether to check that all dex entries have been assigned flags.
@@ -1118,23 +1115,21 @@
// Set of public API stub classpaths. Each classpath is formed by a list
// of DEX/APK files in the order they appear on the classpath.
- std::vector<std::vector<std::string>> stub_classpaths_;
+ std::vector<std::pair<std::string, ApiList>> stub_classpaths_;
- // Paths to text files which contain the lists of API members.
- std::string api_list_path_;
-
- // Paths to text files to which we will output list of all API members.
- std::string out_public_path_;
- std::string out_private_path_;
+ // Path to CSV file containing the list of API members and their flags.
+ // This could be both an input and output path.
+ std::string api_flags_path_;
};
+} // namespace hiddenapi
} // namespace art
int main(int argc, char** argv) {
- art::original_argc = argc;
- art::original_argv = argv;
+ art::hiddenapi::original_argc = argc;
+ art::hiddenapi::original_argv = argv;
android::base::InitLogging(argv);
art::MemMap::Init();
- art::HiddenApi().Run(argc, argv);
+ art::hiddenapi::HiddenApi().Run(argc, argv);
return EXIT_SUCCESS;
}
diff --git a/tools/hiddenapi/hiddenapi_test.cc b/tools/hiddenapi/hiddenapi_test.cc
index 2689eed..7ef5b3d 100644
--- a/tools/hiddenapi/hiddenapi_test.cc
+++ b/tools/hiddenapi/hiddenapi_test.cc
@@ -138,7 +138,7 @@
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 @@
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 2af7b50..1dae93a 100644
--- a/tools/veridex/hidden_api.cc
+++ b/tools/veridex/hidden_api.cc
@@ -30,13 +30,12 @@
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 d6a6438..3c7f29a 100644
--- a/tools/veridex/hidden_api.h
+++ b/tools/veridex/hidden_api.h
@@ -37,11 +37,11 @@
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 46ab8aa..3b6c7f9 100644
--- a/tools/veridex/veridex.cc
+++ b/tools/veridex/veridex.cc
@@ -255,7 +255,7 @@
<< 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;
}