Merge "dexdiag: Replace libpagemap with libmeminfo"
diff --git a/build/apex/ld.config.txt b/build/apex/ld.config.txt
index 9e49d76..9e709d8 100644
--- a/build/apex/ld.config.txt
+++ b/build/apex/ld.config.txt
@@ -8,7 +8,7 @@
dir.runtime = /apex/com.android.runtime/bin/
[runtime]
-additional.namespaces = platform
+additional.namespaces = platform,conscrypt
# Keep in sync with runtime namespace in /system/etc/ld.config.txt.
namespace.default.isolated = true
@@ -28,3 +28,17 @@
namespace.platform.link.default.shared_libs += libnativebridge.so
namespace.platform.link.default.shared_libs += libnativehelper.so
namespace.platform.link.default.shared_libs += libnativeloader.so
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = platform
+namespace.conscrypt.link.platform.shared_libs = libc.so
+namespace.conscrypt.link.platform.shared_libs += libm.so
+namespace.conscrypt.link.platform.shared_libs += libdl.so
diff --git a/build/apex/runtests.sh b/build/apex/runtests.sh
index b2aa4df..4c0e772 100755
--- a/build/apex/runtests.sh
+++ b/build/apex/runtests.sh
@@ -17,6 +17,11 @@
# Run Android Runtime APEX tests.
+# Status of whole test script.
+exit_status=0
+# Status of current test suite.
+test_status=0
+
function say {
echo "$0: $*"
}
@@ -107,32 +112,42 @@
fi
}
+function fail_check {
+ echo "$0: FAILED: $*"
+ test_status=1
+ exit_status=1
+}
+
+function check_file {
+ [[ -f "$mount_point/$1" ]] || fail_check "Cannot find file '$1' in mounted image"
+}
+
function check_binary {
- [[ -x "$mount_point/bin/$1" ]] || die "Cannot find binary '$1' in mounted image"
+ [[ -x "$mount_point/bin/$1" ]] || fail_check "Cannot find binary '$1' in mounted image"
}
function check_multilib_binary {
# TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
# the precision of this test?
[[ -x "$mount_point/bin/${1}32" ]] || [[ -x "$mount_point/bin/${1}64" ]] \
- || die "Cannot find binary '$1' in mounted image"
+ || fail_check "Cannot find binary '$1' in mounted image"
}
function check_binary_symlink {
- [[ -h "$mount_point/bin/$1" ]] || die "Cannot find symbolic link '$1' in mounted image"
+ [[ -h "$mount_point/bin/$1" ]] || fail_check "Cannot find symbolic link '$1' in mounted image"
}
function check_library {
# TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
# the precision of this test?
[[ -f "$mount_point/lib/$1" ]] || [[ -f "$mount_point/lib64/$1" ]] \
- || die "Cannot find library '$1' in mounted image"
+ || fail_check "Cannot find library '$1' in mounted image"
}
# Check contents of APEX payload located in `$mount_point`.
function check_release_contents {
- # Check that the mounted image contains a manifest.
- [[ -f "$mount_point/apex_manifest.json" ]] || die "no manifest"
+ # Check that the mounted image contains an APEX manifest.
+ check_file apex_manifest.json
# Check that the mounted image contains ART base binaries.
check_multilib_binary dalvikvm
@@ -251,6 +266,7 @@
# -----------------------------------------------------------
apex_module="com.android.runtime.release"
+test_status=0
say "Processing APEX package $apex_module"
@@ -276,13 +292,14 @@
trap - EXIT
cleanup_target
-say "$apex_module tests passed"
+[[ "$test_status" = 0 ]] && say "$apex_module tests passed"
echo
# Testing debug APEX package (com.android.runtime.debug).
# -------------------------------------------------------
apex_module="com.android.runtime.debug"
+test_status=0
say "Processing APEX package $apex_module"
@@ -312,7 +329,7 @@
trap - EXIT
cleanup_target
-say "$apex_module tests passed"
+[[ "$test_status" = 0 ]] && say "$apex_module tests passed"
echo
@@ -353,6 +370,7 @@
}
apex_module="com.android.runtime.host"
+test_status=0
say "Processing APEX package $apex_module"
@@ -379,7 +397,8 @@
trap - EXIT
cleanup_host
-say "$apex_module tests passed"
+[[ "$test_status" = 0 ]] && say "$apex_module tests passed"
+[[ "$exit_status" = 0 ]] && say "All Android Runtime APEX tests passed"
-say "All Android Runtime APEX tests passed"
+exit $exit_status
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 1725154..478ecdf 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -427,6 +427,7 @@
gc::CollectorType collector_type_ = gc::kCollectorTypeDefault;
bool verify_pre_gc_heap_ = false;
bool verify_pre_sweeping_heap_ = kIsDebugBuild;
+ bool generational_cc = kEnableGenerationalCCByDefault;
bool verify_post_gc_heap_ = false;
bool verify_pre_gc_rosalloc_ = kIsDebugBuild;
bool verify_pre_sweeping_rosalloc_ = false;
@@ -455,6 +456,10 @@
xgc.verify_pre_sweeping_heap_ = true;
} else if (gc_option == "nopresweepingverify") {
xgc.verify_pre_sweeping_heap_ = false;
+ } else if (gc_option == "generational_cc") {
+ xgc.generational_cc = true;
+ } else if (gc_option == "nogenerational_cc") {
+ xgc.generational_cc = false;
} else if (gc_option == "postverify") {
xgc.verify_post_gc_heap_ = true;
} else if (gc_option == "nopostverify") {
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index 3c68389..ce987c1 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -659,7 +659,7 @@
std::string reason;
ASSERT_TRUE(Runtime::Current()->GetJavaVM()->
- LoadNativeLibrary(env_, "", class_loader_, &reason))
+ LoadNativeLibrary(env_, "", class_loader_, nullptr, &reason))
<< reason;
jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 24);
@@ -675,7 +675,7 @@
std::string reason;
ASSERT_TRUE(Runtime::Current()->GetJavaVM()->
- LoadNativeLibrary(env_, "", class_loader_, &reason))
+ LoadNativeLibrary(env_, "", class_loader_, nullptr, &reason))
<< reason;
jint result = env_->CallStaticIntMethod(jklass_, jmethod_, 42);
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index c23524a..a412938 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -1203,6 +1203,15 @@
});
}
+static std::string GetHiddenapiFlagStr(uint32_t hiddenapi_flags) {
+ std::stringstream ss;
+ hiddenapi::ApiList api_list(hiddenapi_flags);
+ api_list.Dump(ss);
+ std::string str_api_list = ss.str();
+ std::transform(str_api_list.begin(), str_api_list.end(), str_api_list.begin(), ::toupper);
+ return str_api_list;
+}
+
/*
* Dumps a method.
*/
@@ -1220,12 +1229,19 @@
char* typeDescriptor = strdup(signature.ToString().c_str());
const char* backDescriptor = dex_file.StringByTypeIdx(pMethodId.class_idx_);
char* accessStr = createAccessFlagStr(flags, kAccessForMethod);
+ const uint32_t hiddenapiFlags = method.GetHiddenapiFlags();
if (gOptions.outputFormat == OUTPUT_PLAIN) {
fprintf(gOutFile, " #%d : (in %s)\n", i, backDescriptor);
fprintf(gOutFile, " name : '%s'\n", name);
fprintf(gOutFile, " type : '%s'\n", typeDescriptor);
fprintf(gOutFile, " access : 0x%04x (%s)\n", flags, accessStr);
+ if (hiddenapiFlags != 0u) {
+ fprintf(gOutFile,
+ " hiddenapi : 0x%04x (%s)\n",
+ hiddenapiFlags,
+ GetHiddenapiFlagStr(hiddenapiFlags).c_str());
+ }
if (method.GetCodeItem() == nullptr) {
fprintf(gOutFile, " code : (none)\n");
} else {
@@ -1330,12 +1346,19 @@
const char* typeDescriptor = dex_file.StringByTypeIdx(field_id.type_idx_);
const char* backDescriptor = dex_file.StringByTypeIdx(field_id.class_idx_);
char* accessStr = createAccessFlagStr(flags, kAccessForField);
+ const uint32_t hiddenapiFlags = field.GetHiddenapiFlags();
if (gOptions.outputFormat == OUTPUT_PLAIN) {
fprintf(gOutFile, " #%d : (in %s)\n", i, backDescriptor);
fprintf(gOutFile, " name : '%s'\n", name);
fprintf(gOutFile, " type : '%s'\n", typeDescriptor);
fprintf(gOutFile, " access : 0x%04x (%s)\n", flags, accessStr);
+ if (hiddenapiFlags != 0u) {
+ fprintf(gOutFile,
+ " hiddenapi : 0x%04x (%s)\n",
+ hiddenapiFlags,
+ GetHiddenapiFlagStr(hiddenapiFlags).c_str());
+ }
if (data != nullptr) {
fputs(" value : ", gOutFile);
dumpEncodedValue(&dex_file, data);
@@ -1488,7 +1511,7 @@
}
// Fields and methods.
- ClassAccessor accessor(*pDexFile, pClassDef);
+ ClassAccessor accessor(*pDexFile, pClassDef, /* parse_hiddenapi_class_data= */ true);
// Prepare data for static fields.
const u1* sData = pDexFile->GetEncodedStaticFieldValuesArray(pClassDef);
diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc
index 143f5b0..268abe4 100644
--- a/dexlayout/dex_writer.cc
+++ b/dexlayout/dex_writer.cc
@@ -469,6 +469,7 @@
DCHECK_EQ(header_->HiddenapiClassDatas().Size(), header_->ClassDefs().Size());
stream->AlignTo(SectionAlignment(DexFile::kDexTypeHiddenapiClassData));
+ ProcessOffset(stream, &header_->HiddenapiClassDatas());
const uint32_t start = stream->Tell();
// Compute offsets for each class def and write the header.
@@ -989,6 +990,15 @@
}
}
+void DexWriter::ProcessOffset(Stream* stream, dex_ir::CollectionBase* item) {
+ if (compute_offsets_) {
+ item->SetOffset(stream->Tell());
+ } else {
+ // Not computing offsets, just use the one in the item.
+ stream->Seek(item->GetOffset());
+ }
+}
+
std::unique_ptr<DexContainer> DexWriter::CreateDexContainer() const {
return std::unique_ptr<DexContainer>(new DexWriter::Container);
}
diff --git a/dexlayout/dex_writer.h b/dexlayout/dex_writer.h
index 98041d3..62247ec 100644
--- a/dexlayout/dex_writer.h
+++ b/dexlayout/dex_writer.h
@@ -271,6 +271,7 @@
// Process an offset, if compute_offset is set, write into the dex ir item, otherwise read the
// existing offset and use that for writing.
void ProcessOffset(Stream* stream, dex_ir::Item* item);
+ void ProcessOffset(Stream* stream, dex_ir::CollectionBase* item);
dex_ir::Header* const header_;
DexLayout* const dex_layout_;
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/libartbase/base/utils.cc b/libartbase/base/utils.cc
index 58d8575..b989d9e 100644
--- a/libartbase/base/utils.cc
+++ b/libartbase/base/utils.cc
@@ -96,10 +96,10 @@
// The byte thresholds at which we display amounts. A byte count is displayed
// in unit U when kUnitThresholds[U] <= bytes < kUnitThresholds[U+1].
static const int64_t kUnitThresholds[] = {
- 0, // B up to...
- 3*1024, // KB up to...
- 2*1024*1024, // MB up to...
- 1024*1024*1024 // GB from here.
+ 0, // B up to...
+ 10*KB, // KB up to...
+ 10*MB, // MB up to...
+ 10LL*GB // GB from here.
};
static const int64_t kBytesPerUnit[] = { 1, KB, MB, GB };
static const char* const kUnitStrings[] = { "B", "KB", "MB", "GB" };
diff --git a/libartbase/base/utils_test.cc b/libartbase/base/utils_test.cc
index c3b61ce..631a225 100644
--- a/libartbase/base/utils_test.cc
+++ b/libartbase/base/utils_test.cc
@@ -23,8 +23,8 @@
class UtilsTest : public testing::Test {};
TEST_F(UtilsTest, PrettySize) {
- EXPECT_EQ("1GB", PrettySize(1 * GB));
- EXPECT_EQ("2GB", PrettySize(2 * GB));
+ EXPECT_EQ("1024MB", PrettySize(1 * GB));
+ EXPECT_EQ("2048MB", PrettySize(2 * GB));
if (sizeof(size_t) > sizeof(uint32_t)) {
EXPECT_EQ("100GB", PrettySize(100 * GB));
}
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/openjdkjvm/OpenjdkJvm.cc b/openjdkjvm/OpenjdkJvm.cc
index b2c4bb7..8297c54 100644
--- a/openjdkjvm/OpenjdkJvm.cc
+++ b/openjdkjvm/OpenjdkJvm.cc
@@ -318,7 +318,8 @@
JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
jstring javaFilename,
- jobject javaLoader) {
+ jobject javaLoader,
+ jclass caller) {
ScopedUtfChars filename(env, javaFilename);
if (filename.c_str() == nullptr) {
return nullptr;
@@ -330,6 +331,7 @@
bool success = vm->LoadNativeLibrary(env,
filename.c_str(),
javaLoader,
+ caller,
&error_msg);
if (success) {
return nullptr;
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/Android.bp b/runtime/Android.bp
index b89eb02..a3081e9 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -396,8 +396,10 @@
"libnativeloader",
"libbacktrace",
"liblog",
- // For atrace, properties, ashmem, set_sched_policy.
+ // For atrace, properties, ashmem.
"libcutils",
+ // For set_sched_policy.
+ "libprocessgroup",
// For common macros.
"libbase",
],
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/gc/collector/concurrent_copying-inl.h b/runtime/gc/collector/concurrent_copying-inl.h
index 1014c0e..2de7910 100644
--- a/runtime/gc/collector/concurrent_copying-inl.h
+++ b/runtime/gc/collector/concurrent_copying-inl.h
@@ -36,8 +36,7 @@
Thread* const self,
mirror::Object* ref,
accounting::ContinuousSpaceBitmap* bitmap) {
- if (kEnableGenerationalConcurrentCopyingCollection
- && !done_scanning_.load(std::memory_order_acquire)) {
+ if (use_generational_cc_ && !done_scanning_.load(std::memory_order_acquire)) {
// Everything in the unevac space should be marked for young generation CC,
// except for large objects.
DCHECK(!young_gen_ || region_space_bitmap_->Test(ref) || region_space_->IsLargeObject(ref))
@@ -130,7 +129,7 @@
mirror::Object* holder,
MemberOffset offset) {
// Cannot have `kNoUnEvac` when Generational CC collection is disabled.
- DCHECK(kEnableGenerationalConcurrentCopyingCollection || !kNoUnEvac);
+ DCHECK(!kNoUnEvac || use_generational_cc_);
if (from_ref == nullptr) {
return nullptr;
}
@@ -172,9 +171,7 @@
return to_ref;
}
case space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace:
- if (kEnableGenerationalConcurrentCopyingCollection
- && kNoUnEvac
- && !region_space_->IsLargeObject(from_ref)) {
+ if (kNoUnEvac && use_generational_cc_ && !region_space_->IsLargeObject(from_ref)) {
if (!kFromGCThread) {
DCHECK(IsMarkedInUnevacFromSpace(from_ref)) << "Returning unmarked object to mutator";
}
@@ -245,8 +242,7 @@
DCHECK(region_space_->IsInUnevacFromSpace(from_ref));
if (kUseBakerReadBarrier && from_ref->GetReadBarrierStateAcquire() == ReadBarrier::GrayState()) {
return true;
- } else if (!kEnableGenerationalConcurrentCopyingCollection
- || done_scanning_.load(std::memory_order_acquire)) {
+ } else if (!use_generational_cc_ || done_scanning_.load(std::memory_order_acquire)) {
// If the card table scanning is not finished yet, then only read-barrier
// state should be checked. Checking the mark bitmap is unreliable as there
// may be some objects - whose corresponding card is dirty - which are
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 8f7b76a..642b12e 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -69,15 +69,19 @@
ConcurrentCopying::ConcurrentCopying(Heap* heap,
bool young_gen,
+ bool use_generational_cc,
const std::string& name_prefix,
bool measure_read_barrier_slow_path)
: GarbageCollector(heap,
name_prefix + (name_prefix.empty() ? "" : " ") +
"concurrent copying"),
- region_space_(nullptr), gc_barrier_(new Barrier(0)),
+ region_space_(nullptr),
+ gc_barrier_(new Barrier(0)),
gc_mark_stack_(accounting::ObjectStack::Create("concurrent copying gc mark stack",
kDefaultGcMarkStackSize,
kDefaultGcMarkStackSize)),
+ use_generational_cc_(use_generational_cc),
+ young_gen_(young_gen),
rb_mark_bit_stack_(accounting::ObjectStack::Create("rb copying gc mark stack",
kReadBarrierMarkStackSize,
kReadBarrierMarkStackSize)),
@@ -100,7 +104,6 @@
region_space_inter_region_bitmap_(nullptr),
non_moving_space_inter_region_bitmap_(nullptr),
reclaimed_bytes_ratio_sum_(0.f),
- young_gen_(young_gen),
skipped_blocks_lock_("concurrent copying bytes blocks lock", kMarkSweepMarkStackLock),
measure_read_barrier_slow_path_(measure_read_barrier_slow_path),
mark_from_read_barrier_measurements_(false),
@@ -119,7 +122,7 @@
num_bytes_allocated_before_gc_(0) {
static_assert(space::RegionSpace::kRegionSize == accounting::ReadBarrierTable::kRegionSize,
"The region space size and the read barrier table region size must match");
- CHECK(kEnableGenerationalConcurrentCopyingCollection || !young_gen_);
+ CHECK(use_generational_cc_ || !young_gen_);
Thread* self = Thread::Current();
{
ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
@@ -138,7 +141,7 @@
pooled_mark_stacks_.push_back(mark_stack);
}
}
- if (kEnableGenerationalConcurrentCopyingCollection) {
+ if (use_generational_cc_) {
// Allocate sweep array free buffer.
std::string error_msg;
sweep_array_free_buffer_mem_map_ = MemMap::MapAnonymous(
@@ -194,7 +197,7 @@
InitializePhase();
// In case of forced evacuation, all regions are evacuated and hence no
// need to compute live_bytes.
- if (kEnableGenerationalConcurrentCopyingCollection && !young_gen_ && !force_evacuate_all_) {
+ if (use_generational_cc_ && !young_gen_ && !force_evacuate_all_) {
MarkingPhase();
}
}
@@ -290,7 +293,7 @@
}
void ConcurrentCopying::CreateInterRegionRefBitmaps() {
- DCHECK(kEnableGenerationalConcurrentCopyingCollection);
+ DCHECK(use_generational_cc_);
DCHECK(region_space_inter_region_bitmap_ == nullptr);
DCHECK(non_moving_space_inter_region_bitmap_ == nullptr);
DCHECK(region_space_ != nullptr);
@@ -325,7 +328,7 @@
CHECK(!space->IsZygoteSpace());
CHECK(!space->IsImageSpace());
CHECK(space == region_space_ || space == heap_->non_moving_space_);
- if (kEnableGenerationalConcurrentCopyingCollection) {
+ if (use_generational_cc_) {
if (space == region_space_) {
region_space_bitmap_ = region_space_->GetMarkBitmap();
} else if (young_gen_ && space->IsContinuousMemMapAllocSpace()) {
@@ -358,7 +361,7 @@
}
}
}
- if (kEnableGenerationalConcurrentCopyingCollection && young_gen_) {
+ if (use_generational_cc_ && young_gen_) {
for (const auto& space : GetHeap()->GetDiscontinuousSpaces()) {
CHECK(space->IsLargeObjectSpace());
space->AsLargeObjectSpace()->CopyLiveToMarked();
@@ -391,7 +394,7 @@
GcCause gc_cause = GetCurrentIteration()->GetGcCause();
force_evacuate_all_ = false;
- if (!kEnableGenerationalConcurrentCopyingCollection || !young_gen_) {
+ if (!use_generational_cc_ || !young_gen_) {
if (gc_cause == kGcCauseExplicit ||
gc_cause == kGcCauseCollectorTransition ||
GetCurrentIteration()->GetClearSoftReferences()) {
@@ -407,7 +410,7 @@
DCHECK(immune_gray_stack_.empty());
}
}
- if (kEnableGenerationalConcurrentCopyingCollection) {
+ if (use_generational_cc_) {
done_scanning_.store(false, std::memory_order_release);
}
BindBitmaps();
@@ -421,7 +424,7 @@
}
LOG(INFO) << "GC end of InitializePhase";
}
- if (kEnableGenerationalConcurrentCopyingCollection && !young_gen_) {
+ if (use_generational_cc_ && !young_gen_) {
region_space_bitmap_->Clear();
}
mark_stack_mode_.store(ConcurrentCopying::kMarkStackModeThreadLocal, std::memory_order_relaxed);
@@ -533,7 +536,7 @@
cc->region_space_->SetFromSpace(
cc->rb_table_,
evac_mode,
- /*clear_live_bytes=*/ !kEnableGenerationalConcurrentCopyingCollection);
+ /*clear_live_bytes=*/ !cc->use_generational_cc_);
}
cc->SwapStacks();
if (ConcurrentCopying::kEnableFromSpaceAccountingCheck) {
@@ -542,7 +545,7 @@
cc->from_space_num_bytes_at_first_pause_ = cc->region_space_->GetBytesAllocated();
}
cc->is_marking_ = true;
- if (kIsDebugBuild && !kEnableGenerationalConcurrentCopyingCollection) {
+ if (kIsDebugBuild && !cc->use_generational_cc_) {
cc->region_space_->AssertAllRegionLiveBytesZeroOrCleared();
}
if (UNLIKELY(Runtime::Current()->IsActiveTransaction())) {
@@ -866,7 +869,7 @@
DCHECK(obj != nullptr);
DCHECK(immune_spaces_.ContainsObject(obj));
// Update the fields without graying it or pushing it onto the mark stack.
- if (kEnableGenerationalConcurrentCopyingCollection && young_gen_) {
+ if (use_generational_cc_ && young_gen_) {
// Young GC does not care about references to unevac space. It is safe to not gray these as
// long as scan immune objects happens after scanning the dirty cards.
Scan<true>(obj);
@@ -1394,7 +1397,7 @@
if (kUseBakerReadBarrier) {
gc_grays_immune_objects_ = false;
}
- if (kEnableGenerationalConcurrentCopyingCollection) {
+ if (use_generational_cc_) {
if (kVerboseMode) {
LOG(INFO) << "GC ScanCardsForSpace";
}
@@ -2152,7 +2155,7 @@
if (!kUseBakerReadBarrier || !region_space_bitmap_->Set(to_ref)) {
// It may be already marked if we accidentally pushed the same object twice due to the racy
// bitmap read in MarkUnevacFromSpaceRegion.
- if (kEnableGenerationalConcurrentCopyingCollection && young_gen_) {
+ if (use_generational_cc_ && young_gen_) {
CHECK(region_space_->IsLargeObject(to_ref));
region_space_->ZeroLiveBytesForLargeObject(to_ref);
}
@@ -2169,7 +2172,7 @@
}
break;
case space::RegionSpace::RegionType::kRegionTypeToSpace:
- if (kEnableGenerationalConcurrentCopyingCollection) {
+ if (use_generational_cc_) {
// Copied to to-space, set the bit so that the next GC can scan objects.
region_space_bitmap_->Set(to_ref);
}
@@ -2214,7 +2217,7 @@
}
}
if (perform_scan) {
- if (kEnableGenerationalConcurrentCopyingCollection && young_gen_) {
+ if (use_generational_cc_ && young_gen_) {
Scan<true>(to_ref);
} else {
Scan<false>(to_ref);
@@ -2373,7 +2376,7 @@
}
void ConcurrentCopying::Sweep(bool swap_bitmaps) {
- if (kEnableGenerationalConcurrentCopyingCollection && young_gen_) {
+ if (use_generational_cc_ && young_gen_) {
// Only sweep objects on the live stack.
SweepArray(heap_->GetLiveStack(), /* swap_bitmaps= */ false);
} else {
@@ -2407,7 +2410,7 @@
// Copied and adapted from MarkSweep::SweepArray.
void ConcurrentCopying::SweepArray(accounting::ObjectStack* allocations, bool swap_bitmaps) {
// This method is only used when Generational CC collection is enabled.
- DCHECK(kEnableGenerationalConcurrentCopyingCollection);
+ DCHECK(use_generational_cc_);
CheckEmptyMarkStack();
TimingLogger::ScopedTiming t("SweepArray", GetTimings());
Thread* self = Thread::Current();
@@ -2891,8 +2894,7 @@
DCHECK(!immune_spaces_.ContainsObject(from_ref)) << "ref=" << from_ref;
if (kUseBakerReadBarrier && from_ref->GetReadBarrierStateAcquire() == ReadBarrier::GrayState()) {
return true;
- } else if (!kEnableGenerationalConcurrentCopyingCollection
- || done_scanning_.load(std::memory_order_acquire)) {
+ } else if (!use_generational_cc_ || done_scanning_.load(std::memory_order_acquire)) {
// Read the comment in IsMarkedInUnevacFromSpace()
accounting::ContinuousSpaceBitmap* mark_bitmap = heap_->GetNonMovingSpace()->GetMarkBitmap();
accounting::LargeObjectBitmap* los_bitmap = nullptr;
@@ -2954,7 +2956,7 @@
explicit RefFieldsVisitor(ConcurrentCopying* collector, Thread* const thread)
: collector_(collector), thread_(thread) {
// Cannot have `kNoUnEvac` when Generational CC collection is disabled.
- DCHECK(kEnableGenerationalConcurrentCopyingCollection || !kNoUnEvac);
+ DCHECK(!kNoUnEvac || collector_->use_generational_cc_);
}
void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */)
@@ -2991,7 +2993,7 @@
template <bool kNoUnEvac>
inline void ConcurrentCopying::Scan(mirror::Object* to_ref) {
// Cannot have `kNoUnEvac` when Generational CC collection is disabled.
- DCHECK(kEnableGenerationalConcurrentCopyingCollection || !kNoUnEvac);
+ DCHECK(!kNoUnEvac || use_generational_cc_);
if (kDisallowReadBarrierDuringScan && !Runtime::Current()->IsActiveTransaction()) {
// Avoid all read barriers during visit references to help performance.
// Don't do this in transaction mode because we may read the old value of an field which may
@@ -3012,7 +3014,7 @@
template <bool kNoUnEvac>
inline void ConcurrentCopying::Process(mirror::Object* obj, MemberOffset offset) {
// Cannot have `kNoUnEvac` when Generational CC collection is disabled.
- DCHECK(kEnableGenerationalConcurrentCopyingCollection || !kNoUnEvac);
+ DCHECK(!kNoUnEvac || use_generational_cc_);
DCHECK_EQ(Thread::Current(), thread_running_gc_);
mirror::Object* ref = obj->GetFieldObject<
mirror::Object, kVerifyNone, kWithoutReadBarrier, false>(offset);
@@ -3386,7 +3388,7 @@
} else {
DCHECK(heap_->non_moving_space_->HasAddress(to_ref));
DCHECK_EQ(bytes_allocated, non_moving_space_bytes_allocated);
- if (!kEnableGenerationalConcurrentCopyingCollection || !young_gen_) {
+ if (!use_generational_cc_ || !young_gen_) {
// Mark it in the live bitmap.
CHECK(!heap_->non_moving_space_->GetLiveBitmap()->AtomicTestAndSet(to_ref));
}
@@ -3482,7 +3484,7 @@
los_bitmap = heap_->GetLargeObjectsSpace()->GetMarkBitmap();
DCHECK(los_bitmap->HasAddress(ref));
}
- if (kEnableGenerationalConcurrentCopyingCollection) {
+ if (use_generational_cc_) {
// The sticky-bit CC collector is only compatible with Baker-style read barriers.
DCHECK(kUseBakerReadBarrier);
// Not done scanning, use AtomicSetReadBarrierPointer.
@@ -3551,11 +3553,11 @@
}
// kVerifyNoMissingCardMarks relies on the region space cards not being cleared to avoid false
// positives.
- if (!kEnableGenerationalConcurrentCopyingCollection && !kVerifyNoMissingCardMarks) {
+ if (!kVerifyNoMissingCardMarks && !use_generational_cc_) {
TimingLogger::ScopedTiming split("ClearRegionSpaceCards", GetTimings());
// We do not currently use the region space cards at all, madvise them away to save ram.
heap_->GetCardTable()->ClearCardRange(region_space_->Begin(), region_space_->Limit());
- } else if (kEnableGenerationalConcurrentCopyingCollection && !young_gen_) {
+ } else if (use_generational_cc_ && !young_gen_) {
region_space_inter_region_bitmap_->Clear();
non_moving_space_inter_region_bitmap_->Clear();
}
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index a41c17a..124713c 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -65,10 +65,11 @@
// pages.
static constexpr bool kGrayDirtyImmuneObjects = true;
- explicit ConcurrentCopying(Heap* heap,
- bool young_gen,
- const std::string& name_prefix = "",
- bool measure_read_barrier_slow_path = false);
+ ConcurrentCopying(Heap* heap,
+ bool young_gen,
+ bool use_generational_cc,
+ const std::string& name_prefix = "",
+ bool measure_read_barrier_slow_path = false);
~ConcurrentCopying();
void RunPhases() override
@@ -90,7 +91,7 @@
void BindBitmaps() REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::heap_bitmap_lock_);
GcType GetGcType() const override {
- return (kEnableGenerationalConcurrentCopyingCollection && young_gen_)
+ return (use_generational_cc_ && young_gen_)
? kGcTypeSticky
: kGcTypePartial;
}
@@ -323,6 +324,19 @@
std::unique_ptr<Barrier> gc_barrier_;
std::unique_ptr<accounting::ObjectStack> gc_mark_stack_;
+ // If true, enable generational collection when using the Concurrent Copying
+ // (CC) collector, i.e. use sticky-bit CC for minor collections and (full) CC
+ // for major collections. Generational CC collection is currently only
+ // compatible with Baker read barriers. Set in Heap constructor.
+ const bool use_generational_cc_;
+
+ // Generational "sticky", only trace through dirty objects in region space.
+ const bool young_gen_;
+
+ // If true, the GC thread is done scanning marked objects on dirty and aged
+ // card (see ConcurrentCopying::CopyingPhase).
+ Atomic<bool> done_scanning_;
+
// The read-barrier mark-bit stack. Stores object references whose
// mark bit has been set by ConcurrentCopying::MarkFromReadBarrier,
// so that this bit can be reset at the end of the collection in
@@ -400,12 +414,6 @@
// reclaimed_bytes_ratio = reclaimed_bytes/num_allocated_bytes per GC cycle
float reclaimed_bytes_ratio_sum_;
- // Generational "sticky", only trace through dirty objects in region space.
- const bool young_gen_;
- // If true, the GC thread is done scanning marked objects on dirty and aged
- // card (see ConcurrentCopying::CopyingPhase).
- Atomic<bool> done_scanning_;
-
// The skipped blocks are memory blocks/chucks that were copies of
// objects that were unused due to lost races (cas failures) at
// object copy/forward pointer install. They are reused.
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index d699da0..5f62d75 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -107,8 +107,9 @@
// Sticky GC throughput adjustment, divided by 4. Increasing this causes sticky GC to occur more
// relative to partial/full GC. This may be desirable since sticky GCs interfere less with mutator
// threads (lower pauses, use less memory bandwidth).
-static constexpr double kStickyGcThroughputAdjustment =
- kEnableGenerationalConcurrentCopyingCollection ? 0.5 : 1.0;
+static double GetStickyGcThroughputAdjustment(bool use_generational_cc) {
+ return use_generational_cc ? 0.5 : 1.0;
+}
// Whether or not we compact the zygote in PreZygoteFork.
static constexpr bool kCompactZygote = kMovingCollector;
// How many reserve entries are at the end of the allocation stack, these are only needed if the
@@ -201,6 +202,7 @@
bool gc_stress_mode,
bool measure_gc_performance,
bool use_homogeneous_space_compaction_for_oom,
+ bool use_generational_cc,
uint64_t min_interval_homogeneous_space_compaction_by_oom,
bool dump_region_info_before_gc,
bool dump_region_info_after_gc)
@@ -288,6 +290,7 @@
pending_collector_transition_(nullptr),
pending_heap_trim_(nullptr),
use_homogeneous_space_compaction_for_oom_(use_homogeneous_space_compaction_for_oom),
+ use_generational_cc_(use_generational_cc),
running_collection_is_blocking_(false),
blocking_gc_count_(0U),
blocking_gc_time_(0U),
@@ -494,7 +497,8 @@
MemMap region_space_mem_map =
space::RegionSpace::CreateMemMap(kRegionSpaceName, capacity_ * 2, request_begin);
CHECK(region_space_mem_map.IsValid()) << "No region space mem map";
- region_space_ = space::RegionSpace::Create(kRegionSpaceName, std::move(region_space_mem_map));
+ region_space_ = space::RegionSpace::Create(
+ kRegionSpaceName, std::move(region_space_mem_map), use_generational_cc_);
AddSpace(region_space_);
} else if (IsMovingGc(foreground_collector_type_) &&
foreground_collector_type_ != kCollectorTypeGSS) {
@@ -652,26 +656,28 @@
if (MayUseCollector(kCollectorTypeCC)) {
concurrent_copying_collector_ = new collector::ConcurrentCopying(this,
/*young_gen=*/false,
+ use_generational_cc_,
"",
measure_gc_performance);
- if (kEnableGenerationalConcurrentCopyingCollection) {
+ if (use_generational_cc_) {
young_concurrent_copying_collector_ = new collector::ConcurrentCopying(
this,
/*young_gen=*/true,
+ use_generational_cc_,
"young",
measure_gc_performance);
}
active_concurrent_copying_collector_ = concurrent_copying_collector_;
DCHECK(region_space_ != nullptr);
concurrent_copying_collector_->SetRegionSpace(region_space_);
- if (kEnableGenerationalConcurrentCopyingCollection) {
+ if (use_generational_cc_) {
young_concurrent_copying_collector_->SetRegionSpace(region_space_);
// At this point, non-moving space should be created.
DCHECK(non_moving_space_ != nullptr);
concurrent_copying_collector_->CreateInterRegionRefBitmaps();
}
garbage_collectors_.push_back(concurrent_copying_collector_);
- if (kEnableGenerationalConcurrentCopyingCollection) {
+ if (use_generational_cc_) {
garbage_collectors_.push_back(young_concurrent_copying_collector_);
}
}
@@ -2262,7 +2268,7 @@
gc_plan_.clear();
switch (collector_type_) {
case kCollectorTypeCC: {
- if (kEnableGenerationalConcurrentCopyingCollection) {
+ if (use_generational_cc_) {
gc_plan_.push_back(collector::kGcTypeSticky);
}
gc_plan_.push_back(collector::kGcTypeFull);
@@ -2739,7 +2745,7 @@
collector = semi_space_collector_;
break;
case kCollectorTypeCC:
- if (kEnableGenerationalConcurrentCopyingCollection) {
+ if (use_generational_cc_) {
// TODO: Other threads must do the flip checkpoint before they start poking at
// active_concurrent_copying_collector_. So we should not concurrency here.
active_concurrent_copying_collector_ = (gc_type == collector::kGcTypeSticky) ?
@@ -3637,19 +3643,21 @@
collector::GcType non_sticky_gc_type = NonStickyGcType();
// Find what the next non sticky collector will be.
collector::GarbageCollector* non_sticky_collector = FindCollectorByGcType(non_sticky_gc_type);
- if (kEnableGenerationalConcurrentCopyingCollection) {
+ if (use_generational_cc_) {
if (non_sticky_collector == nullptr) {
non_sticky_collector = FindCollectorByGcType(collector::kGcTypePartial);
}
CHECK(non_sticky_collector != nullptr);
}
+ double sticky_gc_throughput_adjustment = GetStickyGcThroughputAdjustment(use_generational_cc_);
+
// If the throughput of the current sticky GC >= throughput of the non sticky collector, then
// do another sticky collection next.
// We also check that the bytes allocated aren't over the footprint limit in order to prevent a
// pathological case where dead objects which aren't reclaimed by sticky could get accumulated
// if the sticky GC throughput always remained >= the full/partial throughput.
size_t target_footprint = target_footprint_.load(std::memory_order_relaxed);
- if (current_gc_iteration_.GetEstimatedThroughput() * kStickyGcThroughputAdjustment >=
+ if (current_gc_iteration_.GetEstimatedThroughput() * sticky_gc_throughput_adjustment >=
non_sticky_collector->GetEstimatedMeanThroughput() &&
non_sticky_collector->NumberOfIterations() > 0 &&
bytes_allocated <= target_footprint) {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 52c9386..4c5d896 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -212,6 +212,7 @@
bool gc_stress_mode,
bool measure_gc_performance,
bool use_homogeneous_space_compaction,
+ bool use_generational_cc,
uint64_t min_interval_homogeneous_space_compaction_by_oom,
bool dump_region_info_before_gc,
bool dump_region_info_after_gc);
@@ -532,6 +533,10 @@
return num_bytes_allocated_.load(std::memory_order_relaxed);
}
+ bool GetUseGenerationalCC() const {
+ return use_generational_cc_;
+ }
+
// Returns the number of objects currently allocated.
size_t GetObjectsAllocated() const
REQUIRES(!Locks::heap_bitmap_lock_);
@@ -768,7 +773,7 @@
// Returns the active concurrent copying collector.
collector::ConcurrentCopying* ConcurrentCopyingCollector() {
- if (kEnableGenerationalConcurrentCopyingCollection) {
+ if (use_generational_cc_) {
DCHECK((active_concurrent_copying_collector_ == concurrent_copying_collector_) ||
(active_concurrent_copying_collector_ == young_concurrent_copying_collector_));
} else {
@@ -1477,6 +1482,11 @@
// Whether or not we use homogeneous space compaction to avoid OOM errors.
bool use_homogeneous_space_compaction_for_oom_;
+ // If true, enable generational collection when using the Concurrent Copying
+ // (CC) collector, i.e. use sticky-bit CC for minor collections and (full) CC
+ // for major collections. Set in Heap constructor.
+ const bool use_generational_cc_;
+
// True if the currently running collection has made some thread wait.
bool running_collection_is_blocking_ GUARDED_BY(gc_complete_lock_);
// The number of blocking GC runs.
diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc
index a5ba1dc..5179702 100644
--- a/runtime/gc/space/region_space.cc
+++ b/runtime/gc/space/region_space.cc
@@ -93,11 +93,12 @@
return mem_map;
}
-RegionSpace* RegionSpace::Create(const std::string& name, MemMap&& mem_map) {
- return new RegionSpace(name, std::move(mem_map));
+RegionSpace* RegionSpace::Create(
+ const std::string& name, MemMap&& mem_map, bool use_generational_cc) {
+ return new RegionSpace(name, std::move(mem_map), use_generational_cc);
}
-RegionSpace::RegionSpace(const std::string& name, MemMap&& mem_map)
+RegionSpace::RegionSpace(const std::string& name, MemMap&& mem_map, bool use_generational_cc)
: ContinuousMemMapAllocSpace(name,
std::move(mem_map),
mem_map.Begin(),
@@ -105,6 +106,7 @@
mem_map.End(),
kGcRetentionPolicyAlwaysCollect),
region_lock_("Region lock", kRegionSpaceRegionLock),
+ use_generational_cc_(use_generational_cc),
time_(1U),
num_regions_(mem_map_.Size() / kRegionSize),
num_non_free_regions_(0U),
@@ -179,9 +181,44 @@
return num_regions * kRegionSize;
}
+void RegionSpace::Region::SetAsUnevacFromSpace(bool clear_live_bytes) {
+ // Live bytes are only preserved (i.e. not cleared) during sticky-bit CC collections.
+ DCHECK(GetUseGenerationalCC() || clear_live_bytes);
+ DCHECK(!IsFree() && IsInToSpace());
+ type_ = RegionType::kRegionTypeUnevacFromSpace;
+ if (IsNewlyAllocated()) {
+ // A newly allocated region set as unevac from-space must be
+ // a large or large tail region.
+ DCHECK(IsLarge() || IsLargeTail()) << static_cast<uint>(state_);
+ // Always clear the live bytes of a newly allocated (large or
+ // large tail) region.
+ clear_live_bytes = true;
+ // Clear the "newly allocated" status here, as we do not want the
+ // GC to see it when encountering (and processing) references in the
+ // from-space.
+ //
+ // Invariant: There should be no newly-allocated region in the
+ // from-space (when the from-space exists, which is between the calls
+ // to RegionSpace::SetFromSpace and RegionSpace::ClearFromSpace).
+ is_newly_allocated_ = false;
+ }
+ if (clear_live_bytes) {
+ // Reset the live bytes, as we have made a non-evacuation
+ // decision (possibly based on the percentage of live bytes).
+ live_bytes_ = 0;
+ }
+}
+
+bool RegionSpace::Region::GetUseGenerationalCC() {
+ // We are retrieving the info from Heap, instead of the cached version in
+ // RegionSpace, because accessing the Heap from a Region object is easier
+ // than accessing the RegionSpace.
+ return art::Runtime::Current()->GetHeap()->GetUseGenerationalCC();
+}
+
inline bool RegionSpace::Region::ShouldBeEvacuated(EvacMode evac_mode) {
// Evacuation mode `kEvacModeNewlyAllocated` is only used during sticky-bit CC collections.
- DCHECK(kEnableGenerationalConcurrentCopyingCollection || (evac_mode != kEvacModeNewlyAllocated));
+ DCHECK(GetUseGenerationalCC() || (evac_mode != kEvacModeNewlyAllocated));
DCHECK((IsAllocated() || IsLarge()) && IsInToSpace());
// The region should be evacuated if:
// - the evacuation is forced (`evac_mode == kEvacModeForceAll`); or
@@ -253,7 +290,7 @@
void RegionSpace::ZeroLiveBytesForLargeObject(mirror::Object* obj) {
// This method is only used when Generational CC collection is enabled.
- DCHECK(kEnableGenerationalConcurrentCopyingCollection);
+ DCHECK(use_generational_cc_);
// This code uses a logic similar to the one used in RegionSpace::FreeLarge
// to traverse the regions supporting `obj`.
@@ -292,7 +329,7 @@
EvacMode evac_mode,
bool clear_live_bytes) {
// Live bytes are only preserved (i.e. not cleared) during sticky-bit CC collections.
- DCHECK(kEnableGenerationalConcurrentCopyingCollection || clear_live_bytes);
+ DCHECK(use_generational_cc_ || clear_live_bytes);
++time_;
if (kUseTableLookupReadBarrier) {
DCHECK(rb_table->IsAllCleared());
@@ -336,9 +373,7 @@
// mark-bit otherwise the live_bytes will not be updated in
// ConcurrentCopying::ProcessMarkStackRef() and hence will break the
// logic.
- if (kEnableGenerationalConcurrentCopyingCollection
- && !should_evacuate
- && is_newly_allocated) {
+ if (use_generational_cc_ && !should_evacuate && is_newly_allocated) {
GetMarkBitmap()->Clear(reinterpret_cast<mirror::Object*>(r->Begin()));
}
num_expected_large_tails = RoundUp(r->BytesAllocated(), kRegionSize) / kRegionSize - 1;
@@ -506,7 +541,7 @@
// bitmap. But they cannot do so before we know the next GC cycle will
// be a major one, so this operation happens at the beginning of such a
// major collection, before marking starts.
- if (!kEnableGenerationalConcurrentCopyingCollection) {
+ if (!use_generational_cc_) {
GetLiveBitmap()->ClearRange(
reinterpret_cast<mirror::Object*>(r->Begin()),
reinterpret_cast<mirror::Object*>(r->Begin() + regions_to_clear_bitmap * kRegionSize));
@@ -520,8 +555,7 @@
// `r` when it has an undefined live bytes count (i.e. when
// `r->LiveBytes() == static_cast<size_t>(-1)`) with
// Generational CC.
- if (!kEnableGenerationalConcurrentCopyingCollection ||
- (r->LiveBytes() != static_cast<size_t>(-1))) {
+ if (!use_generational_cc_ || (r->LiveBytes() != static_cast<size_t>(-1))) {
// Only some allocated bytes are live in this unevac region.
// This should only happen for an allocated non-large region.
DCHECK(r->IsAllocated()) << r->State();
@@ -918,7 +952,7 @@
Region* r = ®ions_[region_index];
if (r->IsFree()) {
r->Unfree(this, time_);
- if (kEnableGenerationalConcurrentCopyingCollection) {
+ if (use_generational_cc_) {
// TODO: Add an explanation for this assertion.
DCHECK(!for_evac || !r->is_newly_allocated_);
}
diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h
index a6f501b..d8b54e2 100644
--- a/runtime/gc/space/region_space.h
+++ b/runtime/gc/space/region_space.h
@@ -59,7 +59,7 @@
// guaranteed to be granted, if it is required, the caller should call Begin on the returned
// space to confirm the request was granted.
static MemMap CreateMemMap(const std::string& name, size_t capacity, uint8_t* requested_begin);
- static RegionSpace* Create(const std::string& name, MemMap&& mem_map);
+ static RegionSpace* Create(const std::string& name, MemMap&& mem_map, bool use_generational_cc);
// Allocate `num_bytes`, returns null if the space is full.
mirror::Object* Alloc(Thread* self,
@@ -368,7 +368,7 @@
}
private:
- RegionSpace(const std::string& name, MemMap&& mem_map);
+ RegionSpace(const std::string& name, MemMap&& mem_map, bool use_generational_cc);
class Region {
public:
@@ -523,33 +523,7 @@
// collection, RegionSpace::ClearFromSpace will preserve the space
// used by this region, and tag it as to-space (see
// Region::SetUnevacFromSpaceAsToSpace below).
- void SetAsUnevacFromSpace(bool clear_live_bytes) {
- // Live bytes are only preserved (i.e. not cleared) during sticky-bit CC collections.
- DCHECK(kEnableGenerationalConcurrentCopyingCollection || clear_live_bytes);
- DCHECK(!IsFree() && IsInToSpace());
- type_ = RegionType::kRegionTypeUnevacFromSpace;
- if (IsNewlyAllocated()) {
- // A newly allocated region set as unevac from-space must be
- // a large or large tail region.
- DCHECK(IsLarge() || IsLargeTail()) << static_cast<uint>(state_);
- // Always clear the live bytes of a newly allocated (large or
- // large tail) region.
- clear_live_bytes = true;
- // Clear the "newly allocated" status here, as we do not want the
- // GC to see it when encountering (and processing) references in the
- // from-space.
- //
- // Invariant: There should be no newly-allocated region in the
- // from-space (when the from-space exists, which is between the calls
- // to RegionSpace::SetFromSpace and RegionSpace::ClearFromSpace).
- is_newly_allocated_ = false;
- }
- if (clear_live_bytes) {
- // Reset the live bytes, as we have made a non-evacuation
- // decision (possibly based on the percentage of live bytes).
- live_bytes_ = 0;
- }
- }
+ void SetAsUnevacFromSpace(bool clear_live_bytes);
// Set this region as to-space. Used by RegionSpace::ClearFromSpace.
// This is only valid if it is currently an unevac from-space region.
@@ -562,7 +536,7 @@
ALWAYS_INLINE bool ShouldBeEvacuated(EvacMode evac_mode);
void AddLiveBytes(size_t live_bytes) {
- DCHECK(kEnableGenerationalConcurrentCopyingCollection || IsInUnevacFromSpace());
+ DCHECK(GetUseGenerationalCC() || IsInUnevacFromSpace());
DCHECK(!IsLargeTail());
DCHECK_NE(live_bytes_, static_cast<size_t>(-1));
// For large allocations, we always consider all bytes in the regions live.
@@ -616,6 +590,8 @@
uint64_t GetLongestConsecutiveFreeBytes() const;
private:
+ static bool GetUseGenerationalCC();
+
size_t idx_; // The region's index in the region space.
size_t live_bytes_; // The live bytes. Used to compute the live percent.
uint8_t* begin_; // The begin address of the region.
@@ -738,6 +714,8 @@
Mutex region_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ // Cached version of Heap::use_generational_cc_.
+ const bool use_generational_cc_;
uint32_t time_; // The time as the number of collections since the startup.
size_t num_regions_; // The number of regions in this space.
// The number of non-free regions in this space.
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/jni/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc
index e7b244b..e54b807 100644
--- a/runtime/jni/java_vm_ext.cc
+++ b/runtime/jni/java_vm_ext.cc
@@ -857,6 +857,7 @@
bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
const std::string& path,
jobject class_loader,
+ jclass caller_class,
std::string* error_msg) {
error_msg->clear();
@@ -872,6 +873,7 @@
library = libraries_->Get(path);
}
void* class_loader_allocator = nullptr;
+ std::string caller_location;
{
ScopedObjectAccess soa(env);
// As the incoming class loader is reachable/alive during the call of this function,
@@ -882,6 +884,13 @@
if (class_linker->IsBootClassLoader(soa, loader.Ptr())) {
loader = nullptr;
class_loader = nullptr;
+ if (caller_class != nullptr) {
+ ObjPtr<mirror::Class> caller = soa.Decode<mirror::Class>(caller_class);
+ ObjPtr<mirror::DexCache> dex_cache = caller->GetDexCache();
+ if (dex_cache != nullptr) {
+ caller_location = dex_cache->GetLocation()->ToModifiedUtf8();
+ }
+ }
}
class_loader_allocator = class_linker->GetAllocatorForClassLoader(loader.Ptr());
@@ -964,13 +973,15 @@
const char* path_str = path.empty() ? nullptr : path.c_str();
bool needs_native_bridge = false;
char* nativeloader_error_msg = nullptr;
- void* handle = android::OpenNativeLibrary(env,
- runtime_->GetTargetSdkVersion(),
- path_str,
- class_loader,
- library_path.get(),
- &needs_native_bridge,
- &nativeloader_error_msg);
+ void* handle = android::OpenNativeLibrary(
+ env,
+ runtime_->GetTargetSdkVersion(),
+ path_str,
+ class_loader,
+ (caller_location.empty() ? nullptr : caller_location.c_str()),
+ library_path.get(),
+ &needs_native_bridge,
+ &nativeloader_error_msg);
VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_NOW) returned " << handle << "]";
if (handle == nullptr) {
diff --git a/runtime/jni/java_vm_ext.h b/runtime/jni/java_vm_ext.h
index 408d354..424dd7c 100644
--- a/runtime/jni/java_vm_ext.h
+++ b/runtime/jni/java_vm_ext.h
@@ -101,6 +101,7 @@
bool LoadNativeLibrary(JNIEnv* env,
const std::string& path,
jobject class_loader,
+ jclass caller_class,
std::string* error_msg);
// Unload native libraries with cleared class loaders.
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/parsed_options.cc b/runtime/parsed_options.cc
index 4a04259..6fd691f 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -703,6 +703,7 @@
UsageMessage(stream, " -Xgc:[no]postsweepingverify_rosalloc\n");
UsageMessage(stream, " -Xgc:[no]postverify_rosalloc\n");
UsageMessage(stream, " -Xgc:[no]presweepingverify\n");
+ UsageMessage(stream, " -Xgc:[no]generational_cc\n");
UsageMessage(stream, " -Ximage:filename\n");
UsageMessage(stream, " -Xbootclasspath-locations:bootclasspath\n"
" (override the dex locations of the -Xbootclasspath files)\n");
diff --git a/runtime/parsed_options_test.cc b/runtime/parsed_options_test.cc
index cbb7b82..77d2316 100644
--- a/runtime/parsed_options_test.cc
+++ b/runtime/parsed_options_test.cc
@@ -130,6 +130,23 @@
EXPECT_EQ(gc::kCollectorTypeSS, xgc.collector_type_);
}
+TEST_F(ParsedOptionsTest, ParsedOptionsGenerationalCC) {
+ RuntimeOptions options;
+ options.push_back(std::make_pair("-Xgc:generational_cc", nullptr));
+
+ RuntimeArgumentMap map;
+ bool parsed = ParsedOptions::Parse(options, false, &map);
+ ASSERT_TRUE(parsed);
+ ASSERT_NE(0u, map.Size());
+
+ using Opt = RuntimeArgumentMap;
+
+ EXPECT_TRUE(map.Exists(Opt::GcOption));
+
+ XGcOption xgc = map.GetOrDefault(Opt::GcOption);
+ ASSERT_TRUE(xgc.generational_cc);
+}
+
TEST_F(ParsedOptionsTest, ParsedOptionsInstructionSet) {
using Opt = RuntimeArgumentMap;
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index d79793b..a86bc94 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),
@@ -1239,6 +1240,10 @@
kExtraDefaultHeapGrowthMultiplier;
}
XGcOption xgc_option = runtime_options.GetOrDefault(Opt::GcOption);
+
+ // Generational CC collection is currently only compatible with Baker read barriers.
+ bool use_generational_cc = kUseBakerReadBarrier && xgc_option.generational_cc;
+
heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize),
runtime_options.GetOrDefault(Opt::HeapGrowthLimit),
runtime_options.GetOrDefault(Opt::HeapMinFree),
@@ -1273,6 +1278,7 @@
xgc_option.gcstress_,
xgc_option.measure_,
runtime_options.GetOrDefault(Opt::EnableHSpaceCompactForOOM),
+ use_generational_cc,
runtime_options.GetOrDefault(Opt::HSpaceCompactForOOMMinIntervalsMs),
runtime_options.Exists(Opt::DumpRegionInfoBeforeGC),
runtime_options.Exists(Opt::DumpRegionInfoAfterGC));
@@ -1746,7 +1752,7 @@
// libcore can't because it's the library that implements System.loadLibrary!
{
std::string error_msg;
- if (!java_vm_->LoadNativeLibrary(env, "libjavacore.so", nullptr, &error_msg)) {
+ if (!java_vm_->LoadNativeLibrary(env, "libjavacore.so", nullptr, nullptr, &error_msg)) {
LOG(FATAL) << "LoadNativeLibrary failed for \"libjavacore.so\": " << error_msg;
}
}
@@ -1755,7 +1761,7 @@
? "libopenjdkd.so"
: "libopenjdk.so";
std::string error_msg;
- if (!java_vm_->LoadNativeLibrary(env, kOpenJdkLibrary, nullptr, &error_msg)) {
+ if (!java_vm_->LoadNativeLibrary(env, kOpenJdkLibrary, nullptr, nullptr, &error_msg)) {
LOG(FATAL) << "LoadNativeLibrary failed for \"" << kOpenJdkLibrary << "\": " << error_msg;
}
}
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/runtime/runtime_globals.h b/runtime/runtime_globals.h
index 793291a..81d350b 100644
--- a/runtime/runtime_globals.h
+++ b/runtime/runtime_globals.h
@@ -40,16 +40,24 @@
static constexpr bool kMarkCompactSupport = false && kMovingCollector;
// True if we allow moving classes.
static constexpr bool kMovingClasses = !kMarkCompactSupport;
-// If true, enable generational collection when using the Concurrent Copying
-// (CC) collector, i.e. use sticky-bit CC for minor collections and (full) CC
-// for major collections.
+// When using the Concurrent Copying (CC) collector, if
+// `ART_USE_GENERATIONAL_CC` is true, enable generational collection by default,
+// i.e. use sticky-bit CC for minor collections and (full) CC for major
+// collections.
+// This default value can be overridden with the runtime option
+// `-Xgc:[no]generational_cc`.
//
-// Generational CC collection is currently only compatible with Baker read
-// barriers.
-#if defined(ART_USE_GENERATIONAL_CC) && defined(ART_READ_BARRIER_TYPE_IS_BAKER)
-static constexpr bool kEnableGenerationalConcurrentCopyingCollection = true;
+// TODO(b/67628039): Consider either:
+// - renaming this to a better descriptive name (e.g.
+// `ART_USE_GENERATIONAL_CC_BY_DEFAULT`); or
+// - removing `ART_USE_GENERATIONAL_CC` and having a fixed default value.
+// Any of these changes will require adjusting users of this preprocessor
+// directive and the corresponding build system environment variable (e.g. in
+// ART's continuous testing).
+#ifdef ART_USE_GENERATIONAL_CC
+static constexpr bool kEnableGenerationalCCByDefault = true;
#else
-static constexpr bool kEnableGenerationalConcurrentCopyingCollection = false;
+static constexpr bool kEnableGenerationalCCByDefault = false;
#endif
// If true, enable the tlab allocator by default.
diff --git a/runtime/thread_android.cc b/runtime/thread_android.cc
index 8ff6c52..24864f9 100644
--- a/runtime/thread_android.cc
+++ b/runtime/thread_android.cc
@@ -21,7 +21,7 @@
#include <sys/resource.h>
#include <sys/time.h>
-#include <cutils/sched_policy.h>
+#include <processgroup/sched_policy.h>
#include <utils/threads.h>
#include "base/macros.h"
diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc
index bd53958..cdfe727 100644
--- a/runtime/ti/agent.cc
+++ b/runtime/ti/agent.cc
@@ -123,6 +123,7 @@
Runtime::Current()->GetTargetSdkVersion(),
name_.c_str(),
class_loader,
+ nullptr,
library_path.get(),
&needs_native_bridge,
&nativeloader_error_msg);
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index a19cc92..f61faa3 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -426,7 +426,7 @@
// to make sure these JNI methods are available.
java_lang_Runtime_nativeLoad =
CacheMethod(env, java_lang_Runtime.get(), true, "nativeLoad",
- "(Ljava/lang/String;Ljava/lang/ClassLoader;)"
+ "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Class;)"
"Ljava/lang/String;");
java_lang_reflect_Proxy_init =
CacheMethod(env, java_lang_reflect_Proxy, false, "<init>",
diff --git a/test/202-thread-oome/src/Main.java b/test/202-thread-oome/src/Main.java
index f7df93b..b5c0ce6 100644
--- a/test/202-thread-oome/src/Main.java
+++ b/test/202-thread-oome/src/Main.java
@@ -21,7 +21,7 @@
t.start();
} catch (OutOfMemoryError expected) {
// TODO: fix bionic bug https://b/6702535 so we can check the full detail message.
- if (!expected.getMessage().startsWith("pthread_create (3GB stack) failed: ")) {
+ if (!expected.getMessage().startsWith("pthread_create (3073MB stack) failed: ")) {
throw new AssertionError(expected);
}
}
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..3dc2789 100644
--- a/test/674-hiddenapi/hiddenapi.cc
+++ b/test/674-hiddenapi/hiddenapi.cc
@@ -27,40 +27,67 @@
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;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_setWhitelistAll(JNIEnv*, jclass, jboolean value) {
+ std::vector<std::string> exemptions;
+ if (value != JNI_FALSE) {
+ exemptions.push_back("L");
+ }
+ Runtime::Current()->SetHiddenApiExemptions(exemptions);
}
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..d6a8c6d 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,72 +47,87 @@
// 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"});
- }
+ // Set exemptions to "L" (matches all classes) if we are testing whitelisting.
+ setWhitelistAll(whitelistAllApis);
// Invoke ChildClass.runTest
- Class.forName("ChildClass", true, childLoader)
- .getDeclaredMethod("runTest", String.class, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE)
- .invoke(null, nativeLibCopy, parentInBoot, childInBoot, whitelistAllApis);
-
- VMRuntime.getRuntime().setHiddenApiExemptions(new String[0]);
+ 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);
}
// Routine which tries to figure out the absolute path of our native library.
@@ -146,11 +168,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 +197,8 @@
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();
+ private static native void setWhitelistAll(boolean value);
}
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/libcore_network_failures.txt b/tools/libcore_network_failures.txt
index e7e31db..380f56b 100644
--- a/tools/libcore_network_failures.txt
+++ b/tools/libcore_network_failures.txt
@@ -8,7 +8,7 @@
description: "Ignore failure of network-related tests on new devices running Android O",
result: EXEC_FAILED,
bug: 74725685,
- modes: [device],
+ modes: [device_testdex],
names: ["libcore.libcore.io.OsTest#test_byteBufferPositions_sendto_recvfrom_af_inet",
"libcore.libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithFtpURLConnection",
"libcore.libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithHttpURLConnection",
diff --git a/tools/run-gtests.sh b/tools/run-gtests.sh
new file mode 100755
index 0000000..8585589
--- /dev/null
+++ b/tools/run-gtests.sh
@@ -0,0 +1,43 @@
+#!/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.
+
+# Script to run all gtests located under $ART_TEST_CHROOT/data/nativetest{64}
+
+ADB="${ADB:-adb}"
+all_tests=()
+failing_tests=()
+
+function add_tests {
+ all_tests+=$(${ADB} shell "test -d $ART_TEST_CHROOT/$1 && chroot $ART_TEST_CHROOT find $1 -name \*_test")
+}
+
+function fail {
+ failing_tests+=($1)
+}
+
+add_tests "/data/nativetest"
+add_tests "/data/nativetest64"
+
+for i in $all_tests; do
+ ${ADB} shell "chroot $ART_TEST_CHROOT env LD_LIBRARY_PATH= ANDROID_ROOT='/system' ANDROID_RUNTIME_ROOT=/system $i" || fail $i
+done
+
+if [ -n "$failing_tests" ]; then
+ for i in "${failing_tests[@]}"; do
+ echo "Failed test: $i"
+ done
+ exit 1
+fi
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;
}