Merge "Speed up relative_patcher_arm64_test."
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 599f4aa..66421e2 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -184,7 +184,7 @@
void CommonCompilerTest::CreateCompilerDriver() {
ApplyInstructionSet();
- compiler_options_->boot_image_ = true;
+ compiler_options_->image_type_ = CompilerOptions::ImageType::kBootImage;
compiler_options_->compile_pic_ = false; // Non-PIC boot image is a test configuration.
compiler_options_->SetCompilerFilter(GetCompilerFilter());
compiler_options_->image_classes_.swap(*GetImageClasses());
@@ -345,7 +345,7 @@
}
void CommonCompilerTest::ClearBootImageOption() {
- compiler_options_->boot_image_ = false;
+ compiler_options_->image_type_ = CompilerOptions::ImageType::kNone;
}
} // namespace art
diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc
index 4f83d60..f61e6c4 100644
--- a/compiler/dex/dex_to_dex_decompiler_test.cc
+++ b/compiler/dex/dex_to_dex_decompiler_test.cc
@@ -41,7 +41,7 @@
void CompileAll(jobject class_loader) REQUIRES(!Locks::mutator_lock_) {
TimingLogger timings("CompilerDriverTest::CompileAll", false, false);
TimingLogger::ScopedTiming t(__FUNCTION__, &timings);
- compiler_options_->boot_image_ = false;
+ compiler_options_->image_type_ = CompilerOptions::ImageType::kNone;
compiler_options_->SetCompilerFilter(CompilerFilter::kQuicken);
// Create the main VerifierDeps, here instead of in the compiler since we want to aggregate
// the results for all the dex files, not just the results for the current dex file.
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 685cde3..3610f18 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -44,8 +44,7 @@
no_inline_from_(),
dex_files_for_oat_file_(),
image_classes_(),
- boot_image_(false),
- app_image_(false),
+ image_type_(ImageType::kNone),
compiling_with_core_image_(false),
baseline_(false),
debuggable_(false),
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 2f4e542..12fa251 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -58,6 +58,12 @@
static const size_t kDefaultInlineMaxCodeUnits = 32;
static constexpr size_t kUnsetInlineMaxCodeUnits = -1;
+ enum class ImageType : uint8_t {
+ kNone, // JIT or AOT app compilation producing only an oat file but no image.
+ kBootImage, // Creating boot image.
+ kAppImage, // Creating app image.
+ };
+
CompilerOptions();
~CompilerOptions();
@@ -191,7 +197,7 @@
// Are we compiling a boot image?
bool IsBootImage() const {
- return boot_image_;
+ return image_type_ == ImageType::kBootImage;
}
bool IsBaseline() const {
@@ -200,11 +206,7 @@
// Are we compiling an app image?
bool IsAppImage() const {
- return app_image_;
- }
-
- void DisableAppImage() {
- app_image_ = false;
+ return image_type_ == ImageType::kAppImage;
}
// Returns whether we are compiling against a "core" image, which
@@ -356,8 +358,7 @@
// Must not be empty for real boot image, only for tests pretending to compile boot image.
HashSet<std::string> image_classes_;
- bool boot_image_;
- bool app_image_;
+ ImageType image_type_;
bool compiling_with_core_image_;
bool baseline_;
bool debuggable_;
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 0c463a2..4d45a99 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -446,7 +446,7 @@
void IntrinsicCodeGeneratorARMVIXL::VisitMathRint(HInvoke* invoke) {
DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
ArmVIXLAssembler* assembler = GetAssembler();
- __ Vrintn(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
+ __ Vrintn(F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
}
void IntrinsicLocationsBuilderARMVIXL::VisitMathRoundFloat(HInvoke* invoke) {
@@ -481,7 +481,7 @@
// Handle input < 0 cases.
// If input is negative but not a tie, previous result (round to nearest) is valid.
// If input is a negative tie, change rounding direction to positive infinity, out_reg += 1.
- __ Vrinta(F32, F32, temp1, in_reg);
+ __ Vrinta(F32, temp1, in_reg);
__ Vmov(temp2, 0.5);
__ Vsub(F32, temp1, in_reg, temp1);
__ Vcmp(F32, temp1, temp2);
@@ -2945,7 +2945,7 @@
void IntrinsicCodeGeneratorARMVIXL::VisitMathCeil(HInvoke* invoke) {
ArmVIXLAssembler* assembler = GetAssembler();
DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
- __ Vrintp(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
+ __ Vrintp(F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
}
void IntrinsicLocationsBuilderARMVIXL::VisitMathFloor(HInvoke* invoke) {
@@ -2957,7 +2957,7 @@
void IntrinsicCodeGeneratorARMVIXL::VisitMathFloor(HInvoke* invoke) {
ArmVIXLAssembler* assembler = GetAssembler();
DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
- __ Vrintm(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
+ __ Vrintm(F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
}
void IntrinsicLocationsBuilderARMVIXL::VisitIntegerValueOf(HInvoke* invoke) {
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index e1b23cc..eb44dd7 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -79,7 +79,7 @@
}
void SetupCompilerDriver() {
- compiler_options_->boot_image_ = false;
+ compiler_options_->image_type_ = CompilerOptions::ImageType::kNone;
compiler_driver_->InitializeThreadPools();
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index dc123e4..12a8354 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -729,11 +729,15 @@
void ProcessOptions(ParserOptions* parser_options) {
compiler_options_->compile_pic_ = true; // All AOT compilation is PIC.
- compiler_options_->boot_image_ = !image_filenames_.empty();
- compiler_options_->app_image_ = app_image_fd_ != -1 || !app_image_file_name_.empty();
-
- if (IsAppImage() && IsBootImage()) {
- Usage("Can't have both --image and (--app-image-fd or --app-image-file)");
+ DCHECK(compiler_options_->image_type_ == CompilerOptions::ImageType::kNone);
+ if (!image_filenames_.empty()) {
+ compiler_options_->image_type_ = CompilerOptions::ImageType::kBootImage;
+ }
+ if (app_image_fd_ != -1 || !app_image_file_name_.empty()) {
+ if (compiler_options_->IsBootImage()) {
+ Usage("Can't have both --image and (--app-image-fd or --app-image-file)");
+ }
+ compiler_options_->image_type_ = CompilerOptions::ImageType::kAppImage;
}
if (oat_filenames_.empty() && oat_fd_ == -1) {
@@ -1595,7 +1599,7 @@
// If we need to downgrade the compiler-filter for size reasons.
if (!IsBootImage() && IsVeryLarge(dex_files)) {
// Disable app image to make sure dex2oat unloading is enabled.
- compiler_options_->DisableAppImage();
+ compiler_options_->image_type_ = CompilerOptions::ImageType::kNone;
// If we need to downgrade the compiler-filter for size reasons, do that early before we read
// it below for creating verification callbacks.
@@ -1957,7 +1961,6 @@
image_writer_.reset(new linker::ImageWriter(*compiler_options_,
image_base_,
- IsAppImage(),
image_storage_mode_,
oat_filenames_,
dex_file_oat_index_map_,
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 9d1a4e7..9ef2875 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -216,11 +216,10 @@
// TODO: compile_pic should be a test argument.
std::unique_ptr<ImageWriter> writer(new ImageWriter(*compiler_options_,
kRequestedImageBase,
- /*compile_app_image*/false,
storage_mode,
oat_filename_vector,
dex_file_to_oat_index_map,
- /*dirty_image_objects*/nullptr));
+ /*dirty_image_objects=*/ nullptr));
{
{
jobject class_loader = nullptr;
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index e4fbd17..a3fc1cd 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -145,10 +145,15 @@
// Separate objects into multiple bins to optimize dirty memory use.
static constexpr bool kBinObjects = true;
+ObjPtr<mirror::ClassLoader> ImageWriter::GetClassLoader() {
+ CHECK_EQ(class_loaders_.size(), compiler_options_.IsAppImage() ? 1u : 0u);
+ return compiler_options_.IsAppImage() ? *class_loaders_.begin() : nullptr;
+}
+
// Return true if an object is already in an image space.
bool ImageWriter::IsInBootImage(const void* obj) const {
gc::Heap* const heap = Runtime::Current()->GetHeap();
- if (!compile_app_image_) {
+ if (compiler_options_.IsBootImage()) {
DCHECK(heap->GetBootImageSpaces().empty());
return false;
}
@@ -165,7 +170,7 @@
bool ImageWriter::IsInBootOatFile(const void* ptr) const {
gc::Heap* const heap = Runtime::Current()->GetHeap();
- if (!compile_app_image_) {
+ if (compiler_options_.IsBootImage()) {
DCHECK(heap->GetBootImageSpaces().empty());
return false;
}
@@ -204,7 +209,7 @@
PruneNonImageClasses(); // Remove junk
}
- if (compile_app_image_) {
+ if (compiler_options_.IsAppImage()) {
TimingLogger::ScopedTiming t("ClearDexFileCookies", timings);
// Clear dex file cookies for app images to enable app image determinism. This is required
// since the cookie field contains long pointers to DexFiles which are not deterministic.
@@ -226,7 +231,7 @@
// Used to store information that will later be used to calculate image
// offsets to string references in the AppImage.
std::vector<HeapReferencePointerInfo> string_ref_info;
- if (ClassLinker::kAppImageMayContainStrings && compile_app_image_) {
+ if (ClassLinker::kAppImageMayContainStrings && compiler_options_.IsAppImage()) {
// Count the number of string fields so we can allocate the appropriate
// amount of space in the image section.
TimingLogger::ScopedTiming t("AppImage:CollectStringReferenceInfo", timings);
@@ -248,7 +253,7 @@
}
// Obtain class count for debugging purposes
- if (VLOG_IS_ON(compiler) && compile_app_image_) {
+ if (VLOG_IS_ON(compiler) && compiler_options_.IsAppImage()) {
ScopedObjectAccess soa(self);
size_t app_image_class_count = 0;
@@ -268,7 +273,7 @@
VLOG(compiler) << "Dex2Oat:AppImage:classCount = " << app_image_class_count;
}
- if (ClassLinker::kAppImageMayContainStrings && compile_app_image_) {
+ if (ClassLinker::kAppImageMayContainStrings && compiler_options_.IsAppImage()) {
// Use the string reference information obtained earlier to calculate image
// offsets. These will later be written to the image by Write/CopyMetadata.
TimingLogger::ScopedTiming t("AppImage:CalculateImageOffsets", timings);
@@ -580,7 +585,7 @@
}
void ImageWriter::CopyMetadata() {
- CHECK(compile_app_image_);
+ DCHECK(compiler_options_.IsAppImage());
CHECK_EQ(image_infos_.size(), 1u);
const ImageInfo& image_info = image_infos_.back();
@@ -620,7 +625,7 @@
ObjPtr<mirror::ClassLoader> class_loader = GetClassLoader();
std::vector<ObjPtr<mirror::DexCache>> dex_caches = FindDexCaches(self);
for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) {
- if (compile_app_image_ && IsInBootImage(dex_cache.Ptr())) {
+ if (IsInBootImage(dex_cache.Ptr())) {
continue; // Boot image DexCache is not written to the app image.
}
PreloadDexCache(dex_cache, class_loader);
@@ -642,7 +647,7 @@
CopyAndFixupObjects();
}
- if (compile_app_image_) {
+ if (compiler_options_.IsAppImage()) {
CopyMetadata();
}
@@ -670,7 +675,7 @@
return false;
}
- if (!compile_app_image_ && fchmod(image_file->Fd(), 0644) != 0) {
+ if (!compiler_options_.IsAppImage() && fchmod(image_file->Fd(), 0644) != 0) {
PLOG(ERROR) << "Failed to make image file world readable: " << image_filename;
image_file->Erase();
return EXIT_FAILURE;
@@ -1223,7 +1228,7 @@
std::unordered_set<mirror::Object*>* visited) {
DCHECK(early_exit != nullptr);
DCHECK(visited != nullptr);
- DCHECK(compile_app_image_);
+ DCHECK(compiler_options_.IsAppImage());
if (klass == nullptr || IsInBootImage(klass.Ptr())) {
return false;
}
@@ -1327,7 +1332,8 @@
if (klass == nullptr) {
return false;
}
- if (compile_app_image_ && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
+ if (!compiler_options_.IsBootImage() &&
+ Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
// Already in boot image, return true.
return true;
}
@@ -1335,7 +1341,7 @@
if (!compiler_options_.IsImageClass(klass->GetDescriptor(&temp))) {
return false;
}
- if (compile_app_image_) {
+ if (compiler_options_.IsAppImage()) {
// For app images, we need to prune boot loader classes that are not in the boot image since
// these may have already been loaded when the app image is loaded.
// Keep classes in the boot image space since we don't want to re-resolve these.
@@ -1631,7 +1637,7 @@
VisitClassLoaders(&class_loader_visitor);
VLOG(compiler) << "Pruned " << class_loader_visitor.GetRemovedClassCount() << " classes";
class_loader = class_loader_visitor.GetClassLoader();
- DCHECK_EQ(class_loader != nullptr, compile_app_image_);
+ DCHECK_EQ(class_loader != nullptr, compiler_options_.IsAppImage());
}
// Clear references to removed classes from the DexCaches.
@@ -1694,7 +1700,7 @@
return found.Ptr();
}
}
- if (compile_app_image_) {
+ if (!compiler_options_.IsBootImage()) {
Runtime* const runtime = Runtime::Current();
ObjPtr<mirror::String> found = runtime->GetInternTable()->LookupStrong(self, string);
// If we found it in the runtime intern table it could either be in the boot image or interned
@@ -1791,7 +1797,7 @@
Handle<ObjectArray<Object>> dex_caches(hs.NewHandle(CollectDexCaches(self, oat_index)));
// build an Object[] of the roots needed to restore the runtime
- int32_t image_roots_size = ImageHeader::NumberOfImageRoots(compile_app_image_);
+ int32_t image_roots_size = ImageHeader::NumberOfImageRoots(compiler_options_.IsAppImage());
Handle<ObjectArray<Object>> image_roots(hs.NewHandle(ObjectArray<Object>::Alloc(
self, GetClassRoot<ObjectArray<Object>>(class_linker), image_roots_size)));
image_roots->Set<false>(ImageHeader::kDexCaches, dex_caches.Get());
@@ -1804,14 +1810,14 @@
runtime->GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow());
image_roots->Set<false>(ImageHeader::kNoClassDefFoundError,
runtime->GetPreAllocatedNoClassDefFoundError());
- if (!compile_app_image_) {
+ if (!compiler_options_.IsAppImage()) {
DCHECK(boot_image_live_objects != nullptr);
image_roots->Set<false>(ImageHeader::kBootImageLiveObjects, boot_image_live_objects.Get());
} else {
DCHECK(boot_image_live_objects == nullptr);
}
- for (int32_t i = 0, num = ImageHeader::NumberOfImageRoots(compile_app_image_); i != num; ++i) {
- if (compile_app_image_ && i == ImageHeader::kAppImageClassLoader) {
+ for (int32_t i = 0; i != image_roots_size; ++i) {
+ if (compiler_options_.IsAppImage() && i == ImageHeader::kAppImageClassLoader) {
// image_roots[ImageHeader::kAppImageClassLoader] will be set later for app image.
continue;
}
@@ -1848,7 +1854,7 @@
mirror::Class* as_klass = obj->AsClass();
mirror::DexCache* dex_cache = as_klass->GetDexCache();
DCHECK(!as_klass->IsErroneous()) << as_klass->GetStatus();
- if (compile_app_image_) {
+ if (compiler_options_.IsAppImage()) {
// Extra sanity, no boot loader classes should be left!
CHECK(!IsBootClassLoaderClass(as_klass)) << as_klass->PrettyClass();
}
@@ -1859,7 +1865,7 @@
// belongs.
oat_index = GetOatIndexForDexCache(dex_cache);
ImageInfo& image_info = GetImageInfo(oat_index);
- if (!compile_app_image_) {
+ if (!compiler_options_.IsAppImage()) {
// Note: Avoid locking to prevent lock order violations from root visiting;
// image_info.class_table_ is only accessed from the image writer.
image_info.class_table_->InsertWithoutLocks(as_klass);
@@ -1962,7 +1968,7 @@
// class loader.
mirror::ClassLoader* class_loader = obj->AsClassLoader();
if (class_loader->GetClassTable() != nullptr) {
- DCHECK(compile_app_image_);
+ DCHECK(compiler_options_.IsAppImage());
DCHECK(class_loaders_.empty());
class_loaders_.insert(class_loader);
ImageInfo& image_info = GetImageInfo(oat_index);
@@ -2140,7 +2146,7 @@
Runtime* const runtime = Runtime::Current();
VariableSizedHandleScope handles(self);
MutableHandle<ObjectArray<Object>> boot_image_live_objects = handles.NewHandle(
- compile_app_image_
+ compiler_options_.IsAppImage()
? nullptr
: IntrinsicObjects::AllocateBootImageLiveObjects(self, runtime->GetClassLinker()));
std::vector<Handle<ObjectArray<Object>>> image_roots;
@@ -2175,7 +2181,8 @@
for (auto* m : image_methods_) {
CHECK(m != nullptr);
CHECK(m->IsRuntimeMethod());
- DCHECK_EQ(compile_app_image_, IsInBootImage(m)) << "Trampolines should be in boot image";
+ DCHECK_EQ(!compiler_options_.IsBootImage(), IsInBootImage(m))
+ << "Trampolines should be in boot image";
if (!IsInBootImage(m)) {
AssignMethodOffset(m, NativeObjectRelocationType::kRuntimeMethod, GetDefaultOatIndex());
}
@@ -2227,7 +2234,7 @@
// For app images, there may be objects that are only held live by the boot image. One
// example is finalizer references. Forward these objects so that EnsureBinSlotAssignedCallback
// does not fail any checks.
- if (compile_app_image_) {
+ if (compiler_options_.IsAppImage()) {
for (gc::space::ImageSpace* space : heap->GetBootImageSpaces()) {
DCHECK(space->IsImageSpace());
gc::accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
@@ -2528,7 +2535,7 @@
}
const void* ImageWriter::GetIntrinsicReferenceAddress(uint32_t intrinsic_data) {
- DCHECK(!compile_app_image_);
+ DCHECK(compiler_options_.IsBootImage());
switch (IntrinsicObjects::DecodePatchType(intrinsic_data)) {
case IntrinsicObjects::PatchType::kIntegerValueOfArray: {
const uint8_t* base_address =
@@ -2921,7 +2928,7 @@
FixupClassVisitor visitor(this, copy);
ObjPtr<mirror::Object>(orig)->VisitReferences(visitor, visitor);
- if (kBitstringSubtypeCheckEnabled && compile_app_image_) {
+ if (kBitstringSubtypeCheckEnabled && compiler_options_.IsAppImage()) {
// When we call SubtypeCheck::EnsureInitialize, it Assigns new bitstring
// values to the parent of that class.
//
@@ -3102,7 +3109,7 @@
const uint8_t* ImageWriter::GetOatAddress(StubType type) const {
DCHECK_LE(type, StubType::kLast);
// If we are compiling an app image, we need to use the stubs of the boot image.
- if (compile_app_image_) {
+ if (!compiler_options_.IsBootImage()) {
// Use the current image pointers.
const std::vector<gc::space::ImageSpace*>& image_spaces =
Runtime::Current()->GetHeap()->GetBootImageSpaces();
@@ -3352,7 +3359,7 @@
cur_image_info.oat_data_begin_ = cur_image_info.oat_file_begin_ + oat_data_offset;
cur_image_info.oat_size_ = oat_data_size;
- if (compile_app_image_) {
+ if (compiler_options_.IsAppImage()) {
CHECK_EQ(oat_filenames_.size(), 1u) << "App image should have no next image.";
return;
}
@@ -3391,7 +3398,6 @@
ImageWriter::ImageWriter(
const CompilerOptions& compiler_options,
uintptr_t image_begin,
- bool compile_app_image,
ImageHeader::StorageMode image_storage_mode,
const std::vector<const char*>& oat_filenames,
const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map,
@@ -3399,7 +3405,6 @@
: compiler_options_(compiler_options),
global_image_begin_(reinterpret_cast<uint8_t*>(image_begin)),
image_objects_offset_begin_(0),
- compile_app_image_(compile_app_image),
target_ptr_size_(InstructionSetPointerSize(compiler_options.GetInstructionSet())),
image_infos_(oat_filenames.size()),
dirty_methods_(0u),
@@ -3409,9 +3414,11 @@
oat_filenames_(oat_filenames),
dex_file_oat_index_map_(dex_file_oat_index_map),
dirty_image_objects_(dirty_image_objects) {
+ DCHECK(compiler_options.IsBootImage() || compiler_options.IsAppImage());
CHECK_NE(image_begin, 0U);
std::fill_n(image_methods_, arraysize(image_methods_), nullptr);
- CHECK_EQ(compile_app_image, !Runtime::Current()->GetHeap()->GetBootImageSpaces().empty())
+ CHECK_EQ(compiler_options.IsBootImage(),
+ Runtime::Current()->GetHeap()->GetBootImageSpaces().empty())
<< "Compiling a boot image should occur iff there are no boot image spaces loaded";
}
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index bf89665..3c377a3 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -78,7 +78,6 @@
public:
ImageWriter(const CompilerOptions& compiler_options,
uintptr_t image_begin,
- bool compile_app_image,
ImageHeader::StorageMode image_storage_mode,
const std::vector<const char*>& oat_filenames,
const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map,
@@ -112,10 +111,7 @@
return true;
}
- ObjPtr<mirror::ClassLoader> GetClassLoader() {
- CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u);
- return compile_app_image_ ? *class_loaders_.begin() : nullptr;
- }
+ ObjPtr<mirror::ClassLoader> GetClassLoader();
template <typename T>
T* GetImageAddress(T* object) const REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -754,9 +750,6 @@
// Oat index map for objects.
std::unordered_map<mirror::Object*, uint32_t> oat_index_map_;
- // Boolean flags.
- const bool compile_app_image_;
-
// Size of pointers on the target architecture.
PointerSize target_ptr_size_;
diff --git a/libartbase/base/membarrier.cc b/libartbase/base/membarrier.cc
index def949e..4c86b6b 100644
--- a/libartbase/base/membarrier.cc
+++ b/libartbase/base/membarrier.cc
@@ -25,7 +25,6 @@
#if defined(__BIONIC__)
#include <atomic>
-#include <android/get_device_api_level.h>
#include <linux/membarrier.h>
#define CHECK_MEMBARRIER_CMD(art_value, membarrier_value) \
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index cc4f56c..35379cc 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1543,26 +1543,6 @@
}
}
-// Update the class loader. Should only be used on classes in the image space.
-class UpdateClassLoaderVisitor {
- public:
- UpdateClassLoaderVisitor(gc::space::ImageSpace* space, ObjPtr<mirror::ClassLoader> class_loader)
- : space_(space),
- class_loader_(class_loader) {}
-
- bool operator()(ObjPtr<mirror::Class> klass) const REQUIRES_SHARED(Locks::mutator_lock_) {
- // Do not update class loader for boot image classes where the app image
- // class loader is only the initiating loader but not the defining loader.
- if (klass->GetClassLoader() != nullptr) {
- klass->SetClassLoader(class_loader_);
- }
- return true;
- }
-
- gc::space::ImageSpace* const space_;
- ObjPtr<mirror::ClassLoader> const class_loader_;
-};
-
static std::unique_ptr<const DexFile> OpenOatDexFile(const OatFile* oat_file,
const char* location,
std::string* error_msg)
@@ -2036,9 +2016,17 @@
ScopedTrace trace("AppImage:UpdateClassLoaders");
// Update class loader and resolved strings. If added_class_table is false, the resolved
// strings were forwarded UpdateAppImageClassLoadersAndDexCaches.
- UpdateClassLoaderVisitor visitor(space, class_loader.Get());
+ ObjPtr<mirror::ClassLoader> loader(class_loader.Get());
for (const ClassTable::TableSlot& root : temp_set) {
- visitor(root.Read());
+ // Note: We probably don't need the read barrier unless we copy the app image objects into
+ // the region space.
+ ObjPtr<mirror::Class> klass(root.Read());
+ // Do not update class loader for boot image classes where the app image
+ // class loader is only the initiating loader but not the defining loader.
+ // Avoid read barrier since we are comparing against null.
+ if (klass->GetClassLoader<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr) {
+ klass->SetClassLoader</*kCheckTransaction=*/ false>(loader);
+ }
}
}
@@ -2527,6 +2515,35 @@
return ClassPathEntry(nullptr, nullptr);
}
+bool ClassLinker::FindClassInSharedLibraries(ScopedObjectAccessAlreadyRunnable& soa,
+ Thread* self,
+ const char* descriptor,
+ size_t hash,
+ Handle<mirror::ClassLoader> class_loader,
+ /*out*/ ObjPtr<mirror::Class>* result) {
+ ArtField* field =
+ jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+ ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader.Get());
+ if (raw_shared_libraries == nullptr) {
+ return true;
+ }
+
+ StackHandleScope<2> hs(self);
+ Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries(
+ hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>()));
+ MutableHandle<mirror::ClassLoader> temp_loader = hs.NewHandle<mirror::ClassLoader>(nullptr);
+ for (int32_t i = 0; i < shared_libraries->GetLength(); ++i) {
+ temp_loader.Assign(shared_libraries->Get(i));
+ if (!FindClassInBaseDexClassLoader(soa, self, descriptor, hash, temp_loader, result)) {
+ return false; // One of the shared libraries is not supported.
+ }
+ if (*result != nullptr) {
+ return true; // Found the class up the chain.
+ }
+ }
+ return true;
+}
+
bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
Thread* self,
const char* descriptor,
@@ -2542,6 +2559,7 @@
if (IsPathOrDexClassLoader(soa, class_loader)) {
// For regular path or dex class loader the search order is:
// - parent
+ // - shared libraries
// - class loader dex files
// Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
@@ -2554,6 +2572,13 @@
return true; // Found the class up the chain.
}
+ if (!FindClassInSharedLibraries(soa, self, descriptor, hash, class_loader, result)) {
+ return false; // One of the shared library loader is not supported.
+ }
+ if (*result != nullptr) {
+ return true; // Found the class in a shared library.
+ }
+
// Search the current class loader classpath.
*result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader);
return true;
@@ -2562,6 +2587,7 @@
if (IsDelegateLastClassLoader(soa, class_loader)) {
// For delegate last, the search order is:
// - boot class path
+ // - shared libraries
// - class loader dex files
// - parent
*result = FindClassInBootClassLoaderClassPath(self, descriptor, hash);
@@ -2569,6 +2595,13 @@
return true; // The class is part of the boot class path.
}
+ if (!FindClassInSharedLibraries(soa, self, descriptor, hash, class_loader, result)) {
+ return false; // One of the shared library loader is not supported.
+ }
+ if (*result != nullptr) {
+ return true; // Found the class in a shared library.
+ }
+
*result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader);
if (*result != nullptr) {
return true; // Found the class in the current class loader
@@ -5070,8 +5103,10 @@
ArtField* resolved_field = dex_cache->GetResolvedField(field_idx, image_pointer_size_);
if (resolved_field == nullptr) {
// Populating cache of a dex file which defines `klass` should always be allowed.
- DCHECK_EQ(hiddenapi::GetMemberAction(
- field, class_loader.Get(), dex_cache.Get(), hiddenapi::kNone), hiddenapi::kAllow);
+ DCHECK(!hiddenapi::ShouldDenyAccessToMember(
+ field,
+ hiddenapi::AccessContext(class_loader.Get(), dex_cache.Get()),
+ hiddenapi::AccessMethod::kNone));
dex_cache->SetResolvedField(field_idx, field, image_pointer_size_);
} else {
DCHECK_EQ(field, resolved_field);
@@ -8102,8 +8137,9 @@
}
DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr);
if (resolved != nullptr &&
- hiddenapi::GetMemberAction(
- resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) {
+ hiddenapi::ShouldDenyAccessToMember(resolved,
+ hiddenapi::AccessContext(class_loader, dex_cache),
+ hiddenapi::AccessMethod::kLinking)) {
resolved = nullptr;
}
if (resolved != nullptr) {
@@ -8133,11 +8169,9 @@
ObjPtr<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_) {
return method == nullptr ||
- hiddenapi::GetMemberAction(method,
- class_loader,
- dex_cache,
- hiddenapi::kNone) // do not print warnings
- == hiddenapi::kDeny;
+ hiddenapi::ShouldDenyAccessToMember(method,
+ hiddenapi::AccessContext(class_loader, dex_cache),
+ hiddenapi::AccessMethod::kNone); // no warnings
}
ArtMethod* ClassLinker::FindIncompatibleMethod(ObjPtr<mirror::Class> klass,
@@ -8273,8 +8307,10 @@
resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_);
}
if (resolved != nullptr &&
- hiddenapi::GetMemberAction(
- resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) {
+ hiddenapi::ShouldDenyAccessToMember(
+ resolved,
+ hiddenapi::AccessContext(class_loader.Get(), dex_cache.Get()),
+ hiddenapi::AccessMethod::kLinking)) {
resolved = nullptr;
}
return resolved;
@@ -8373,8 +8409,9 @@
}
if (resolved != nullptr &&
- hiddenapi::GetMemberAction(
- resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) {
+ hiddenapi::ShouldDenyAccessToMember(resolved,
+ hiddenapi::AccessContext(class_loader, dex_cache),
+ hiddenapi::AccessMethod::kLinking)) {
resolved = nullptr;
}
@@ -8399,8 +8436,9 @@
resolved = mirror::Class::FindField(self, klass, name, type);
if (resolved != nullptr &&
- hiddenapi::GetMemberAction(
- resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) {
+ hiddenapi::ShouldDenyAccessToMember(resolved,
+ hiddenapi::AccessContext(class_loader, dex_cache),
+ hiddenapi::AccessMethod::kLinking)) {
resolved = nullptr;
}
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 60e68d5..a65299a 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -868,6 +868,15 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
+ bool FindClassInSharedLibraries(ScopedObjectAccessAlreadyRunnable& soa,
+ Thread* self,
+ const char* descriptor,
+ size_t hash,
+ Handle<mirror::ClassLoader> class_loader,
+ /*out*/ ObjPtr<mirror::Class>* result)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_);
+
// Finds the class in the classpath of the given class loader. It only searches the class loader
// dex files and does not recurse into its parent.
// The method checks that the provided class loader is either a PathClassLoader or a
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index ed3a18d..b11e368 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -66,7 +66,7 @@
}
runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
- if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kNoChecks) {
+ if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kDisabled) {
argv.push_back("--runtime-arg");
argv.push_back("-Xhidden-api-checks");
}
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index f355276..3b7b938 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -44,38 +44,53 @@
static inline std::ostream& operator<<(std::ostream& os, AccessMethod value) {
switch (value) {
- case kNone:
+ case AccessMethod::kNone:
LOG(FATAL) << "Internal access to hidden API should not be logged";
UNREACHABLE();
- case kReflection:
+ case AccessMethod::kReflection:
os << "reflection";
break;
- case kJNI:
+ case AccessMethod::kJNI:
os << "JNI";
break;
- case kLinking:
+ case AccessMethod::kLinking:
os << "linking";
break;
}
return os;
}
-static constexpr bool EnumsEqual(EnforcementPolicy policy, hiddenapi::ApiList apiList) {
- return static_cast<int>(policy) == static_cast<int>(apiList);
-}
-
-// GetMemberAction-related static_asserts.
-static_assert(
- EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, hiddenapi::ApiList::kDarkGreylist) &&
- EnumsEqual(EnforcementPolicy::kBlacklistOnly, hiddenapi::ApiList::kBlacklist),
- "Mismatch between EnforcementPolicy and ApiList enums");
-static_assert(
- EnforcementPolicy::kJustWarn < EnforcementPolicy::kDarkGreyAndBlackList &&
- EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly,
- "EnforcementPolicy values ordering not correct");
-
namespace detail {
+// Do not change the values of items in this enum, as they are written to the
+// event log for offline analysis. Any changes will interfere with that analysis.
+enum AccessContextFlags {
+ // Accessed member is a field if this bit is set, else a method
+ kMemberIsField = 1 << 0,
+ // Indicates if access was denied to the member, instead of just printing a warning.
+ kAccessDenied = 1 << 1,
+};
+
+static int32_t GetMaxAllowedSdkVersionForApiList(ApiList api_list) {
+ SdkCodes sdk = SdkCodes::kVersionNone;
+ switch (api_list) {
+ case ApiList::kWhitelist:
+ case ApiList::kLightGreylist:
+ sdk = SdkCodes::kVersionUnlimited;
+ break;
+ case ApiList::kDarkGreylist:
+ sdk = SdkCodes::kVersionO_MR1;
+ break;
+ case ApiList::kBlacklist:
+ sdk = SdkCodes::kVersionNone;
+ break;
+ case ApiList::kNoList:
+ LOG(FATAL) << "Unexpected value";
+ UNREACHABLE();
+ }
+ return static_cast<int32_t>(sdk);
+}
+
MemberSignature::MemberSignature(ArtField* field) {
class_name_ = field->GetDeclaringClass()->GetDescriptor(&tmp_);
member_name_ = field->GetName();
@@ -137,6 +152,7 @@
LOG(WARNING) << "Accessing hidden " << (type_ == kField ? "field " : "method ")
<< Dumpable<MemberSignature>(*this) << " (" << list << ", " << access_method << ")";
}
+
#ifdef ART_TARGET_ANDROID
// Convert an AccessMethod enum to a value for logging from the proto enum.
// This method may look odd (the enum values are current the same), but it
@@ -145,13 +161,13 @@
// future.
inline static int32_t GetEnumValueForLog(AccessMethod access_method) {
switch (access_method) {
- case kNone:
+ case AccessMethod::kNone:
return android::metricslogger::ACCESS_METHOD_NONE;
- case kReflection:
+ case AccessMethod::kReflection:
return android::metricslogger::ACCESS_METHOD_REFLECTION;
- case kJNI:
+ case AccessMethod::kJNI:
return android::metricslogger::ACCESS_METHOD_JNI;
- case kLinking:
+ case AccessMethod::kLinking:
return android::metricslogger::ACCESS_METHOD_LINKING;
default:
DCHECK(false);
@@ -159,9 +175,9 @@
}
#endif
-void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action action_taken) {
+void MemberSignature::LogAccessToEventLog(AccessMethod access_method, bool access_denied) {
#ifdef ART_TARGET_ANDROID
- if (access_method == kLinking || access_method == kNone) {
+ if (access_method == AccessMethod::kLinking || access_method == AccessMethod::kNone) {
// Linking warnings come from static analysis/compilation of the bytecode
// and can contain false positives (i.e. code that is never run). We choose
// not to log these in the event log.
@@ -170,7 +186,7 @@
}
ComplexEventLogger log_maker(ACTION_HIDDEN_API_ACCESSED);
log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_METHOD, GetEnumValueForLog(access_method));
- if (action_taken == kDeny) {
+ if (access_denied) {
log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_DENIED, 1);
}
const std::string& package_name = Runtime::Current()->GetProcessPackageName();
@@ -183,10 +199,42 @@
log_maker.Record();
#else
UNUSED(access_method);
- UNUSED(action_taken);
+ UNUSED(access_denied);
#endif
}
+void MemberSignature::NotifyHiddenApiListener(AccessMethod access_method) {
+ if (access_method != AccessMethod::kReflection && access_method != AccessMethod::kJNI) {
+ // We can only up-call into Java during reflection and JNI down-calls.
+ return;
+ }
+
+ Runtime* runtime = Runtime::Current();
+ if (!runtime->IsAotCompiler()) {
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+
+ ScopedLocalRef<jobject> consumer_object(soa.Env(),
+ soa.Env()->GetStaticObjectField(
+ WellKnownClasses::dalvik_system_VMRuntime,
+ WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer));
+ // If the consumer is non-null, we call back to it to let it know that we
+ // have encountered an API that's in one of our lists.
+ if (consumer_object != nullptr) {
+ std::ostringstream member_signature_str;
+ Dump(member_signature_str);
+
+ ScopedLocalRef<jobject> signature_str(
+ soa.Env(),
+ soa.Env()->NewStringUTF(member_signature_str.str().c_str()));
+
+ // Call through to Consumer.accept(String memberSignature);
+ soa.Env()->CallVoidMethod(consumer_object.get(),
+ WellKnownClasses::java_util_function_Consumer_accept,
+ signature_str.get());
+ }
+ }
+}
+
static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtField*) {
return true;
}
@@ -205,116 +253,68 @@
}
template<typename T>
-Action GetMemberActionImpl(T* member,
- hiddenapi::ApiList api_list,
- Action action,
- AccessMethod access_method) {
- DCHECK_NE(action, kAllow);
-
- // Get the signature, we need it later.
- MemberSignature member_signature(member);
+bool ShouldDenyAccessToMemberImpl(T* member,
+ hiddenapi::ApiList api_list,
+ AccessMethod access_method) {
+ DCHECK(member != nullptr);
Runtime* runtime = Runtime::Current();
+ EnforcementPolicy policy = runtime->GetHiddenApiEnforcementPolicy();
+
+ const bool deny_access =
+ (policy == EnforcementPolicy::kEnabled) &&
+ (runtime->GetTargetSdkVersion() > GetMaxAllowedSdkVersionForApiList(api_list));
+
+ MemberSignature member_signature(member);
// Check for an exemption first. Exempted APIs are treated as white list.
- // We only do this if we're about to deny, or if the app is debuggable. This is because:
- // - we only print a warning for light greylist violations for debuggable apps
- // - for non-debuggable apps, there is no distinction between light grey & whitelisted APIs.
- // - we want to avoid the overhead of checking for exemptions for light greylisted APIs whenever
- // possible.
- const bool shouldWarn = kLogAllAccesses || runtime->IsJavaDebuggable();
- if (shouldWarn || action == kDeny) {
- if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
- action = kAllow;
- // Avoid re-examining the exemption list next time.
- // Note this results in no warning for the member, which seems like what one would expect.
- // Exemptions effectively adds new members to the whitelist.
- MaybeWhitelistMember(runtime, member);
- return kAllow;
- }
+ if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
+ // Avoid re-examining the exemption list next time.
+ // Note this results in no warning for the member, which seems like what one would expect.
+ // Exemptions effectively adds new members to the whitelist.
+ MaybeWhitelistMember(runtime, member);
+ return false;
+ }
- if (access_method != kNone) {
- // Print a log message with information about this class member access.
- // We do this if we're about to block access, or the app is debuggable.
+ if (access_method != AccessMethod::kNone) {
+ // Print a log message with information about this class member access.
+ // We do this if we're about to deny access, or the app is debuggable.
+ if (kLogAllAccesses || deny_access || runtime->IsJavaDebuggable()) {
member_signature.WarnAboutAccess(access_method, api_list);
}
- }
- if (kIsTargetBuild && !kIsTargetLinux) {
- uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate();
- // Assert that RAND_MAX is big enough, to ensure sampling below works as expected.
- static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small");
- if (eventLogSampleRate != 0 &&
- (static_cast<uint32_t>(std::rand()) & 0xffff) < eventLogSampleRate) {
- member_signature.LogAccessToEventLog(access_method, action);
+ // If there is a StrictMode listener, notify it about this violation.
+ member_signature.NotifyHiddenApiListener(access_method);
+
+ // If event log sampling is enabled, report this violation.
+ if (kIsTargetBuild && !kIsTargetLinux) {
+ uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate();
+ // Assert that RAND_MAX is big enough, to ensure sampling below works as expected.
+ static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small");
+ if (eventLogSampleRate != 0 &&
+ (static_cast<uint32_t>(std::rand()) & 0xffff) < eventLogSampleRate) {
+ member_signature.LogAccessToEventLog(access_method, deny_access);
+ }
+ }
+
+ // If this access was not denied, move the member into whitelist and skip
+ // the warning the next time the member is accessed.
+ if (!deny_access) {
+ MaybeWhitelistMember(runtime, member);
}
}
- if (action == kDeny) {
- // Block access
- return action;
- }
-
- // Allow access to this member but print a warning.
- DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast);
-
- if (access_method != kNone) {
- // Depending on a runtime flag, we might move the member into whitelist and
- // skip the warning the next time the member is accessed.
- MaybeWhitelistMember(runtime, member);
-
- // If this action requires a UI warning, set the appropriate flag.
- if (shouldWarn &&
- (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag())) {
- runtime->SetPendingHiddenApiWarning(true);
- }
- }
-
- return action;
+ return deny_access;
}
// Need to instantiate this.
-template Action GetMemberActionImpl<ArtField>(ArtField* member,
- hiddenapi::ApiList api_list,
- Action action,
- AccessMethod access_method);
-template Action GetMemberActionImpl<ArtMethod>(ArtMethod* member,
- hiddenapi::ApiList api_list,
- Action action,
- AccessMethod access_method);
+template bool ShouldDenyAccessToMemberImpl<ArtField>(ArtField* member,
+ hiddenapi::ApiList api_list,
+ AccessMethod access_method);
+template bool ShouldDenyAccessToMemberImpl<ArtMethod>(ArtMethod* member,
+ hiddenapi::ApiList api_list,
+ AccessMethod access_method);
} // namespace detail
-template<typename T>
-void NotifyHiddenApiListener(T* member) {
- Runtime* runtime = Runtime::Current();
- if (!runtime->IsAotCompiler()) {
- ScopedObjectAccessUnchecked soa(Thread::Current());
-
- ScopedLocalRef<jobject> consumer_object(soa.Env(),
- soa.Env()->GetStaticObjectField(
- WellKnownClasses::dalvik_system_VMRuntime,
- WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer));
- // If the consumer is non-null, we call back to it to let it know that we
- // have encountered an API that's in one of our lists.
- if (consumer_object != nullptr) {
- detail::MemberSignature member_signature(member);
- std::ostringstream member_signature_str;
- member_signature.Dump(member_signature_str);
-
- ScopedLocalRef<jobject> signature_str(
- soa.Env(),
- soa.Env()->NewStringUTF(member_signature_str.str().c_str()));
-
- // Call through to Consumer.accept(String memberSignature);
- soa.Env()->CallVoidMethod(consumer_object.get(),
- WellKnownClasses::java_util_function_Consumer_accept,
- signature_str.get());
- }
- }
-}
-
-template void NotifyHiddenApiListener<ArtMethod>(ArtMethod* member);
-template void NotifyHiddenApiListener<ArtField>(ArtField* member);
-
} // namespace hiddenapi
} // namespace art
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 57f1a59..ed00e2a 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -32,11 +32,10 @@
// This must be kept in sync with ApplicationInfo.ApiEnforcementPolicy in
// frameworks/base/core/java/android/content/pm/ApplicationInfo.java
enum class EnforcementPolicy {
- kNoChecks = 0,
+ kDisabled = 0,
kJustWarn = 1, // keep checks enabled, but allow everything (enables logging)
- kDarkGreyAndBlackList = 2, // ban dark grey & blacklist
- kBlacklistOnly = 3, // ban blacklist violations only
- kMax = kBlacklistOnly,
+ kEnabled = 2, // ban dark grey & blacklist
+ kMax = kEnabled,
};
inline EnforcementPolicy EnforcementPolicyFromInt(int api_policy_int) {
@@ -45,56 +44,59 @@
return static_cast<EnforcementPolicy>(api_policy_int);
}
-enum Action {
- kAllow,
- kAllowButWarn,
- kAllowButWarnAndToast,
- kDeny
-};
-
-enum AccessMethod {
+enum class AccessMethod {
kNone, // internal test that does not correspond to an actual access by app
kReflection,
kJNI,
kLinking,
};
-// Do not change the values of items in this enum, as they are written to the
-// event log for offline analysis. Any changes will interfere with that analysis.
-enum AccessContextFlags {
- // Accessed member is a field if this bit is set, else a method
- kMemberIsField = 1 << 0,
- // Indicates if access was denied to the member, instead of just printing a warning.
- kAccessDenied = 1 << 1,
+struct AccessContext {
+ public:
+ explicit AccessContext(bool is_trusted) : is_trusted_(is_trusted) {}
+
+ explicit AccessContext(ObjPtr<mirror::Class> klass) : is_trusted_(GetIsTrusted(klass)) {}
+
+ AccessContext(ObjPtr<mirror::ClassLoader> class_loader, ObjPtr<mirror::DexCache> dex_cache)
+ : is_trusted_(GetIsTrusted(class_loader, dex_cache)) {}
+
+ bool IsTrusted() const { return is_trusted_; }
+
+ private:
+ static bool GetIsTrusted(ObjPtr<mirror::ClassLoader> class_loader,
+ 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;
+ }
+
+ static bool GetIsTrusted(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(!klass.IsNull());
+
+ if (klass->ShouldSkipHiddenApiChecks() && Runtime::Current()->IsJavaDebuggable()) {
+ // Class is known, it is marked trusted and we are in debuggable mode.
+ return true;
+ }
+
+ // Check other aspects of the context.
+ return GetIsTrusted(klass->GetClassLoader(), klass->GetDexCache());
+ }
+
+ bool is_trusted_;
};
-inline Action GetActionFromAccessFlags(ApiList api_list) {
- if (api_list == ApiList::kWhitelist) {
- return kAllow;
- }
-
- EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
- if (policy == EnforcementPolicy::kNoChecks) {
- // Exit early. Nothing to enforce.
- return kAllow;
- }
-
- // if policy is "just warn", always warn. We returned above for whitelist APIs.
- if (policy == EnforcementPolicy::kJustWarn) {
- return kAllowButWarn;
- }
- DCHECK(policy >= EnforcementPolicy::kDarkGreyAndBlackList);
- // The logic below relies on equality of values in the enums EnforcementPolicy and
- // ApiList, and their ordering. Assertions are in hidden_api.cc.
- if (static_cast<int>(policy) > static_cast<int>(api_list)) {
- return api_list == ApiList::kDarkGreylist
- ? kAllowButWarnAndToast
- : kAllowButWarn;
- } else {
- return kDeny;
- }
-}
-
class ScopedHiddenApiEnforcementPolicySetting {
public:
explicit ScopedHiddenApiEnforcementPolicySetting(EnforcementPolicy new_policy)
@@ -114,6 +116,13 @@
// Implementation details. DO NOT ACCESS DIRECTLY.
namespace detail {
+enum class SdkCodes {
+ kVersionNone = std::numeric_limits<int32_t>::min(),
+ kVersionUnlimited = std::numeric_limits<int32_t>::max(),
+ kVersionO_MR1 = 27,
+ kVersionP = 28,
+};
+
// Class to encapsulate the signature of a member (ArtField or ArtMethod). This
// is used as a helper when matching prefixes, and when logging the signature.
class MemberSignature {
@@ -146,59 +155,31 @@
void WarnAboutAccess(AccessMethod access_method, ApiList list);
- void LogAccessToEventLog(AccessMethod access_method, Action action_taken);
+ void LogAccessToEventLog(AccessMethod access_method, bool access_denied);
+
+ // Calls back into managed code to notify VMRuntime.nonSdkApiUsageConsumer that
+ // |member| was accessed. This is usually called when an API is on the black,
+ // dark grey or light grey lists. Given that the callback can execute arbitrary
+ // code, a call to this method can result in thread suspension.
+ void NotifyHiddenApiListener(AccessMethod access_method);
};
template<typename T>
-Action GetMemberActionImpl(T* member,
- ApiList api_list,
- Action action,
- AccessMethod access_method)
+bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_);
-// Returns true if the caller is either loaded by the boot strap class loader or comes from
-// a dex file located in ${ANDROID_ROOT}/framework/.
-ALWAYS_INLINE
-inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller,
- ObjPtr<mirror::ClassLoader> caller_class_loader,
- ObjPtr<mirror::DexCache> caller_dex_cache)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (caller_class_loader.IsNull()) {
- // Boot class loader.
- return true;
- }
-
- if (!caller_dex_cache.IsNull()) {
- const DexFile* caller_dex_file = caller_dex_cache->GetDexFile();
- if (caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile()) {
- // Caller is in a platform dex file.
- return true;
- }
- }
-
- if (!caller.IsNull() &&
- caller->ShouldSkipHiddenApiChecks() &&
- Runtime::Current()->IsJavaDebuggable()) {
- // We are in debuggable mode and this caller has been marked trusted.
- return true;
- }
-
- return false;
-}
-
} // namespace detail
-// Returns true if access to `member` should be denied to the caller of the
-// reflective query. The decision is based on whether the caller is trusted or
-// not. Because different users of this function determine this in a different
-// way, `fn_caller_is_trusted(self)` is called and should return true if the
-// caller is allowed to access the platform.
+// Returns true if access to `member` should be denied in the given context.
+// The decision is based on whether the caller is in a trusted context or not.
+// Because determining the access context can be expensive, a lambda function
+// "fn_get_access_context" is lazily invoked after other criteria have been
+// considered.
// This function might print warnings into the log if the member is hidden.
template<typename T>
-inline Action GetMemberAction(T* member,
- Thread* self,
- std::function<bool(Thread*)> fn_caller_is_trusted,
- AccessMethod access_method)
+inline bool ShouldDenyAccessToMember(T* member,
+ std::function<AccessContext()> fn_get_access_context,
+ AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(member != nullptr);
@@ -210,53 +191,34 @@
// results, e.g. print whitelist warnings (b/78327881).
ApiList api_list = member->GetHiddenApiAccessFlags();
- Action action = GetActionFromAccessFlags(member->GetHiddenApiAccessFlags());
- if (action == kAllow) {
- // Nothing to do.
- return action;
+ // Exit early if member is on the whitelist.
+ if (api_list == ApiList::kWhitelist) {
+ return false;
}
- // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access.
+ // Check if caller is exempted from access checks.
// This can be *very* expensive. Save it for last.
- if (fn_caller_is_trusted(self)) {
- // Caller is trusted. Exit.
- return kAllow;
+ if (fn_get_access_context().IsTrusted()) {
+ return false;
}
- // Member is hidden and caller is not in the platform.
- return detail::GetMemberActionImpl(member, api_list, action, access_method);
+ // Member is hidden and caller is not exempted. Enter slow path.
+ return detail::ShouldDenyAccessToMemberImpl(member, api_list, access_method);
}
-inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller) REQUIRES_SHARED(Locks::mutator_lock_) {
- return !caller.IsNull() &&
- detail::IsCallerTrusted(caller, caller->GetClassLoader(), caller->GetDexCache());
-}
-
-// Returns true if access to `member` should be denied to a caller loaded with
-// `caller_class_loader`.
-// This function might print warnings into the log if the member is hidden.
+// 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 Action GetMemberAction(T* member,
- ObjPtr<mirror::ClassLoader> caller_class_loader,
- ObjPtr<mirror::DexCache> caller_dex_cache,
- AccessMethod access_method)
+inline bool ShouldDenyAccessToMember(T* member,
+ AccessContext access_context,
+ AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
- bool is_caller_trusted =
- detail::IsCallerTrusted(/* caller= */ nullptr, caller_class_loader, caller_dex_cache);
- return GetMemberAction(member,
- /* thread= */ nullptr,
- [is_caller_trusted] (Thread*) { return is_caller_trusted; },
- access_method);
+ return ShouldDenyAccessToMember(
+ member,
+ [&] () REQUIRES_SHARED(Locks::mutator_lock_) { return access_context; },
+ access_method);
}
-// Calls back into managed code to notify VMRuntime.nonSdkApiUsageConsumer that
-// |member| was accessed. This is usually called when an API is on the black,
-// dark grey or light grey lists. Given that the callback can execute arbitrary
-// code, a call to this method can result in thread suspension.
-template<typename T> void NotifyHiddenApiListener(T* member)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
-
} // namespace hiddenapi
} // namespace art
diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc
index 1727af0..627d9a7 100644
--- a/runtime/hidden_api_test.cc
+++ b/runtime/hidden_api_test.cc
@@ -23,7 +23,7 @@
namespace art {
using hiddenapi::detail::MemberSignature;
-using hiddenapi::GetActionFromAccessFlags;
+using hiddenapi::detail::ShouldDenyAccessToMemberImpl;
class HiddenApiTest : public CommonRuntimeTest {
protected:
@@ -68,6 +68,15 @@
return art_field;
}
+ bool ShouldDenyAccess(hiddenapi::ApiList list) REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Choose parameters such that there are no side effects (AccessMethod::kNone)
+ // and that the member is not on the exemptions list (here we choose one which
+ // is not even in boot class path).
+ return ShouldDenyAccessToMemberImpl(/* member= */ class1_field1_,
+ list,
+ /* access_method= */ hiddenapi::AccessMethod::kNone);
+ }
+
protected:
Thread* self_;
jobject jclass_loader_;
@@ -88,41 +97,33 @@
};
TEST_F(HiddenApiTest, CheckGetActionFromRuntimeFlags) {
- runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kWhitelist), hiddenapi::kAllow);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kLightGreylist), hiddenapi::kAllow);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kDarkGreylist), hiddenapi::kAllow);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kBlacklist), hiddenapi::kAllow);
+ ScopedObjectAccess soa(self_);
+
+ runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDisabled);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), false);
runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kJustWarn);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kWhitelist),
- hiddenapi::kAllow);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kLightGreylist),
- hiddenapi::kAllowButWarn);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kDarkGreylist),
- hiddenapi::kAllowButWarn);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kBlacklist),
- hiddenapi::kAllowButWarn);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), false);
- runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kWhitelist),
- hiddenapi::kAllow);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kLightGreylist),
- hiddenapi::kAllowButWarn);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kDarkGreylist),
- hiddenapi::kDeny);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kBlacklist),
- hiddenapi::kDeny);
+ runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+ runtime_->SetTargetSdkVersion(static_cast<int32_t>(hiddenapi::detail::SdkCodes::kVersionO_MR1));
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), true);
- runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kWhitelist),
- hiddenapi::kAllow);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kLightGreylist),
- hiddenapi::kAllowButWarn);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kDarkGreylist),
- hiddenapi::kAllowButWarnAndToast);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kBlacklist),
- hiddenapi::kDeny);
+ runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+ runtime_->SetTargetSdkVersion(static_cast<int32_t>(hiddenapi::detail::SdkCodes::kVersionP));
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), true);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), true);
}
TEST_F(HiddenApiTest, CheckMembersRead) {
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 9bc2179..e292a76 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -182,15 +182,16 @@
}
template<typename T>
-static ALWAYS_INLINE bool ShouldBlockAccessToMember(T* member, ShadowFrame* frame)
+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::kReflection;
- return hiddenapi::GetMemberAction(
+ constexpr hiddenapi::AccessMethod access_method = hiddenapi::AccessMethod::kReflection;
+ return hiddenapi::ShouldDenyAccessToMember(
member,
- frame->GetMethod()->GetDeclaringClass()->GetClassLoader(),
- frame->GetMethod()->GetDeclaringClass()->GetDexCache(),
- access_method) == hiddenapi::kDeny;
+ [&]() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return hiddenapi::AccessContext(frame->GetMethod()->GetDeclaringClass());
+ },
+ access_method);
}
void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self,
@@ -297,7 +298,7 @@
auto* cl = Runtime::Current()->GetClassLinker();
if (cl->EnsureInitialized(self, h_klass, true, true)) {
ArtMethod* cons = h_klass->FindConstructor("()V", cl->GetImagePointerSize());
- if (cons != nullptr && ShouldBlockAccessToMember(cons, shadow_frame)) {
+ if (cons != nullptr && ShouldDenyAccessToMember(cons, shadow_frame)) {
cons = nullptr;
}
if (cons != nullptr) {
@@ -342,7 +343,7 @@
}
}
}
- if (found != nullptr && ShouldBlockAccessToMember(found, shadow_frame)) {
+ if (found != nullptr && ShouldDenyAccessToMember(found, shadow_frame)) {
found = nullptr;
}
if (found == nullptr) {
@@ -407,7 +408,7 @@
self, klass, name, args);
}
}
- if (method != nullptr && ShouldBlockAccessToMember(method->GetArtMethod(), shadow_frame)) {
+ if (method != nullptr && ShouldDenyAccessToMember(method->GetArtMethod(), shadow_frame)) {
method = nullptr;
}
result->SetL(method);
@@ -445,7 +446,7 @@
}
}
if (constructor != nullptr &&
- ShouldBlockAccessToMember(constructor->GetArtMethod(), shadow_frame)) {
+ ShouldDenyAccessToMember(constructor->GetArtMethod(), shadow_frame)) {
constructor = nullptr;
}
result->SetL(constructor);
diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc
index 5e01b79..af86cc0 100644
--- a/runtime/jni/jni_internal.cc
+++ b/runtime/jni/jni_internal.cc
@@ -84,20 +84,20 @@
// things not rendering correctly. E.g. b/16858794
static constexpr bool kWarnJniAbort = false;
-static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
- return hiddenapi::IsCallerTrusted(GetCallingClass(self, /* num_frames= */ 1));
-}
-
template<typename T>
-ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
+ALWAYS_INLINE static bool ShouldDenyAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- hiddenapi::Action action = hiddenapi::GetMemberAction(
- member, self, IsCallerTrusted, hiddenapi::kJNI);
- if (action != hiddenapi::kAllow) {
- hiddenapi::NotifyHiddenApiListener(member);
- }
-
- return action == hiddenapi::kDeny;
+ return hiddenapi::ShouldDenyAccessToMember(
+ member,
+ [&]() REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Construct AccessContext from the first calling class on stack.
+ // If the calling class cannot be determined, e.g. unattached threads,
+ // we conservatively assume the caller is trusted.
+ ObjPtr<mirror::Class> caller = GetCallingClass(self, /* num_frames */ 1);
+ return caller.IsNull() ? hiddenapi::AccessContext(/* is_trusted= */ true)
+ : hiddenapi::AccessContext(caller);
+ },
+ hiddenapi::AccessMethod::kJNI);
}
// Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set
@@ -259,7 +259,7 @@
} else {
method = c->FindClassMethod(name, sig, pointer_size);
}
- if (method != nullptr && ShouldBlockAccessToMember(method, soa.Self())) {
+ if (method != nullptr && ShouldDenyAccessToMember(method, soa.Self())) {
method = nullptr;
}
if (method == nullptr || method->IsStatic() != is_static) {
@@ -338,7 +338,7 @@
} else {
field = c->FindInstanceField(name, field_type->GetDescriptor(&temp));
}
- if (field != nullptr && ShouldBlockAccessToMember(field, soa.Self())) {
+ if (field != nullptr && ShouldDenyAccessToMember(field, soa.Self())) {
field = nullptr;
}
if (field == nullptr) {
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 6b9ba8c..185ae3b 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -1102,6 +1102,16 @@
return component->IsPrimitive() || component->CannotBeAssignedFromOtherTypes();
}
+template <bool kCheckTransaction>
+inline void Class::SetClassLoader(ObjPtr<ClassLoader> new_class_loader) {
+ if (kCheckTransaction && Runtime::Current()->IsActiveTransaction()) {
+ SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader);
+ } else {
+ DCHECK(!Runtime::Current()->IsActiveTransaction());
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader);
+ }
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index e33e407..83d76a9 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -427,14 +427,6 @@
return GetClassRoot<mirror::Throwable>()->IsAssignableFrom(this);
}
-void Class::SetClassLoader(ObjPtr<ClassLoader> new_class_loader) {
- if (Runtime::Current()->IsActiveTransaction()) {
- SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader);
- } else {
- SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader);
- }
-}
-
template <typename SignatureType>
static inline ArtMethod* FindInterfaceMethodWithSignature(ObjPtr<Class> klass,
const StringPiece& name,
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index bb54b3d..c38cc86 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -628,6 +628,7 @@
ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ClassLoader* GetClassLoader() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_);
+ template <bool kCheckTransaction = true>
void SetClassLoader(ObjPtr<ClassLoader> new_cl) REQUIRES_SHARED(Locks::mutator_lock_);
static constexpr MemberOffset DexCacheOffset() {
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 4d3ad62..e4bc1b7 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -304,8 +304,7 @@
// Our system thread ID, etc, has changed so reset Thread state.
thread->InitAfterFork();
runtime_flags = EnableDebugFeatures(runtime_flags);
- hiddenapi::EnforcementPolicy api_enforcement_policy = hiddenapi::EnforcementPolicy::kNoChecks;
- bool dedupe_hidden_api_warnings = true;
+ hiddenapi::EnforcementPolicy api_enforcement_policy = hiddenapi::EnforcementPolicy::kDisabled;
if ((runtime_flags & DISABLE_VERIFIER) != 0) {
Runtime::Current()->DisableVerifier();
@@ -372,14 +371,14 @@
}
}
- bool do_hidden_api_checks = api_enforcement_policy != hiddenapi::EnforcementPolicy::kNoChecks;
+ bool do_hidden_api_checks = api_enforcement_policy != hiddenapi::EnforcementPolicy::kDisabled;
DCHECK(!(is_system_server && do_hidden_api_checks))
<< "SystemServer should be forked with EnforcementPolicy::kDisable";
DCHECK(!(is_zygote && do_hidden_api_checks))
<< "Child zygote processes should be forked with EnforcementPolicy::kDisable";
Runtime::Current()->SetHiddenApiEnforcementPolicy(api_enforcement_policy);
- Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe_hidden_api_warnings);
- if (api_enforcement_policy != hiddenapi::EnforcementPolicy::kNoChecks &&
+ Runtime::Current()->SetDedupeHiddenApiWarnings(true);
+ if (api_enforcement_policy != hiddenapi::EnforcementPolicy::kDisabled &&
Runtime::Current()->GetHiddenApiEventLogSampleRate() != 0) {
// Hidden API checks are enabled, and we are sampling access for the event log. Initialize the
// random seed, to ensure the sampling is actually random. We do this post-fork, as doing it
@@ -387,9 +386,6 @@
std::srand(static_cast<uint32_t>(NanoTime()));
}
- // Clear the hidden API warning flag, in case it was set.
- Runtime::Current()->SetPendingHiddenApiWarning(false);
-
if (is_zygote) {
// If creating a child-zygote, do not call into the runtime's post-fork logic.
// Doing so would spin up threads for Binder and JDWP. Instead, the Java side
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index c7b8ad4..612a4b3 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -54,11 +54,12 @@
namespace art {
-// Returns true if the first caller outside of the Class class or java.lang.invoke package
-// is in a platform DEX file.
-static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
- // Walk the stack and find the first frame not from java.lang.Class and not from java.lang.invoke.
- // This is very expensive. Save this till the last.
+// Walks the stack, finds the caller of this reflective call and returns
+// a hiddenapi AccessContext formed from its declaring class.
+static hiddenapi::AccessContext GetReflectionCaller(Thread* self)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Walk the stack and find the first frame not from java.lang.Class and not
+ // from java.lang.invoke. This is very expensive. Save this till the last.
struct FirstExternalCallerVisitor : public StackVisitor {
explicit FirstExternalCallerVisitor(Thread* thread)
: StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
@@ -102,50 +103,42 @@
FirstExternalCallerVisitor visitor(self);
visitor.WalkStack();
- return visitor.caller != nullptr &&
- hiddenapi::IsCallerTrusted(visitor.caller->GetDeclaringClass());
-}
-// Returns true if the first non-ClassClass caller up the stack is not allowed to
-// access hidden APIs. This can be *very* expensive. Never call this in a loop.
-ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
- return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerTrusted(self);
+ // Construct AccessContext from the calling class found on the stack.
+ // If the calling class cannot be determined, e.g. unattached threads,
+ // we conservatively assume the caller is trusted.
+ ObjPtr<mirror::Class> caller = (visitor.caller == nullptr)
+ ? nullptr : visitor.caller->GetDeclaringClass();
+ return caller.IsNull() ? hiddenapi::AccessContext(/* is_trusted= */ true)
+ : hiddenapi::AccessContext(caller);
}
// Returns true if the first non-ClassClass caller up the stack should not be
// allowed access to `member`.
template<typename T>
-ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
+ALWAYS_INLINE static bool ShouldDenyAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- hiddenapi::Action action = hiddenapi::GetMemberAction(
- member, self, IsCallerTrusted, hiddenapi::kReflection);
- if (action != hiddenapi::kAllow) {
- hiddenapi::NotifyHiddenApiListener(member);
- }
-
- return action == hiddenapi::kDeny;
+ return hiddenapi::ShouldDenyAccessToMember(
+ member,
+ [&]() REQUIRES_SHARED(Locks::mutator_lock_) { return GetReflectionCaller(self); },
+ hiddenapi::AccessMethod::kReflection);
}
// Returns true if a class member should be discoverable with reflection given
// the criteria. Some reflection calls only return public members
// (public_only == true), some members should be hidden from non-boot class path
-// callers (enforce_hidden_api == true).
+// callers (hiddenapi_context).
template<typename T>
ALWAYS_INLINE static bool IsDiscoverable(bool public_only,
- bool enforce_hidden_api,
+ hiddenapi::AccessContext access_context,
T* member)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (public_only && ((member->GetAccessFlags() & kAccPublic) == 0)) {
return false;
}
- return hiddenapi::GetMemberAction(member,
- nullptr,
- [enforce_hidden_api] (Thread*) { return !enforce_hidden_api; },
- hiddenapi::kNone)
- != hiddenapi::kDeny;
+ return !hiddenapi::ShouldDenyAccessToMember(
+ member, access_context, hiddenapi::AccessMethod::kNone);
}
ALWAYS_INLINE static inline ObjPtr<mirror::Class> DecodeClass(
@@ -266,15 +259,15 @@
IterationRange<StrideIterator<ArtField>> ifields = klass->GetIFields();
IterationRange<StrideIterator<ArtField>> sfields = klass->GetSFields();
size_t array_size = klass->NumInstanceFields() + klass->NumStaticFields();
- bool enforce_hidden_api = ShouldEnforceHiddenApi(self);
+ hiddenapi::AccessContext hiddenapi_context = GetReflectionCaller(self);
// Lets go subtract all the non discoverable fields.
for (ArtField& field : ifields) {
- if (!IsDiscoverable(public_only, enforce_hidden_api, &field)) {
+ if (!IsDiscoverable(public_only, hiddenapi_context, &field)) {
--array_size;
}
}
for (ArtField& field : sfields) {
- if (!IsDiscoverable(public_only, enforce_hidden_api, &field)) {
+ if (!IsDiscoverable(public_only, hiddenapi_context, &field)) {
--array_size;
}
}
@@ -285,7 +278,7 @@
return nullptr;
}
for (ArtField& field : ifields) {
- if (IsDiscoverable(public_only, enforce_hidden_api, &field)) {
+ if (IsDiscoverable(public_only, hiddenapi_context, &field)) {
auto* reflect_field = mirror::Field::CreateFromArtField<kRuntimePointerSize>(self,
&field,
force_resolve);
@@ -300,7 +293,7 @@
}
}
for (ArtField& field : sfields) {
- if (IsDiscoverable(public_only, enforce_hidden_api, &field)) {
+ if (IsDiscoverable(public_only, hiddenapi_context, &field)) {
auto* reflect_field = mirror::Field::CreateFromArtField<kRuntimePointerSize>(self,
&field,
force_resolve);
@@ -459,8 +452,7 @@
StackHandleScope<1> hs(soa.Self());
Handle<mirror::Field> field = hs.NewHandle(GetPublicFieldRecursive(
soa.Self(), DecodeClass(soa, javaThis), name_string));
- if (field.Get() == nullptr ||
- ShouldBlockAccessToMember(field->GetArtField(), soa.Self())) {
+ if (field.Get() == nullptr || ShouldDenyAccessToMember(field->GetArtField(), soa.Self())) {
return nullptr;
}
return soa.AddLocalReference<jobject>(field.Get());
@@ -477,7 +469,7 @@
Handle<mirror::Class> h_klass = hs.NewHandle(DecodeClass(soa, javaThis));
Handle<mirror::Field> result =
hs.NewHandle(GetDeclaredField(soa.Self(), h_klass.Get(), h_string.Get()));
- if (result == nullptr || ShouldBlockAccessToMember(result->GetArtField(), soa.Self())) {
+ if (result == nullptr || ShouldDenyAccessToMember(result->GetArtField(), soa.Self())) {
std::string name_str = h_string->ToModifiedUtf8();
if (name_str == "value" && h_klass->IsStringClass()) {
// We log the error for this specific case, as the user might just swallow the exception.
@@ -509,19 +501,19 @@
soa.Self(),
DecodeClass(soa, javaThis),
soa.Decode<mirror::ObjectArray<mirror::Class>>(args)));
- if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) {
+ if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) {
return nullptr;
}
return soa.AddLocalReference<jobject>(result.Get());
}
static ALWAYS_INLINE inline bool MethodMatchesConstructor(
- ArtMethod* m, bool public_only, bool enforce_hidden_api)
+ ArtMethod* m, bool public_only, hiddenapi::AccessContext hiddenapi_context)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(m != nullptr);
return m->IsConstructor() &&
!m->IsStatic() &&
- IsDiscoverable(public_only, enforce_hidden_api, m);
+ IsDiscoverable(public_only, hiddenapi_context, m);
}
static jobjectArray Class_getDeclaredConstructorsInternal(
@@ -529,12 +521,12 @@
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<2> hs(soa.Self());
bool public_only = (publicOnly != JNI_FALSE);
- bool enforce_hidden_api = ShouldEnforceHiddenApi(soa.Self());
+ hiddenapi::AccessContext hiddenapi_context = GetReflectionCaller(soa.Self());
Handle<mirror::Class> h_klass = hs.NewHandle(DecodeClass(soa, javaThis));
size_t constructor_count = 0;
// Two pass approach for speed.
for (auto& m : h_klass->GetDirectMethods(kRuntimePointerSize)) {
- constructor_count += MethodMatchesConstructor(&m, public_only, enforce_hidden_api) ? 1u : 0u;
+ constructor_count += MethodMatchesConstructor(&m, public_only, hiddenapi_context) ? 1u : 0u;
}
auto h_constructors = hs.NewHandle(mirror::ObjectArray<mirror::Constructor>::Alloc(
soa.Self(), GetClassRoot<mirror::ObjectArray<mirror::Constructor>>(), constructor_count));
@@ -544,7 +536,7 @@
}
constructor_count = 0;
for (auto& m : h_klass->GetDirectMethods(kRuntimePointerSize)) {
- if (MethodMatchesConstructor(&m, public_only, enforce_hidden_api)) {
+ if (MethodMatchesConstructor(&m, public_only, hiddenapi_context)) {
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
DCHECK(!Runtime::Current()->IsActiveTransaction());
ObjPtr<mirror::Constructor> constructor =
@@ -571,7 +563,7 @@
DecodeClass(soa, javaThis),
soa.Decode<mirror::String>(name),
soa.Decode<mirror::ObjectArray<mirror::Class>>(args)));
- if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) {
+ if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) {
return nullptr;
}
return soa.AddLocalReference<jobject>(result.Get());
@@ -582,7 +574,7 @@
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<2> hs(soa.Self());
- bool enforce_hidden_api = ShouldEnforceHiddenApi(soa.Self());
+ hiddenapi::AccessContext hiddenapi_context = GetReflectionCaller(soa.Self());
bool public_only = (publicOnly != JNI_FALSE);
Handle<mirror::Class> klass = hs.NewHandle(DecodeClass(soa, javaThis));
@@ -591,7 +583,7 @@
uint32_t modifiers = m.GetAccessFlags();
// Add non-constructor declared methods.
if ((modifiers & kAccConstructor) == 0 &&
- IsDiscoverable(public_only, enforce_hidden_api, &m)) {
+ IsDiscoverable(public_only, hiddenapi_context, &m)) {
++num_methods;
}
}
@@ -605,7 +597,7 @@
for (ArtMethod& m : klass->GetDeclaredMethods(kRuntimePointerSize)) {
uint32_t modifiers = m.GetAccessFlags();
if ((modifiers & kAccConstructor) == 0 &&
- IsDiscoverable(public_only, enforce_hidden_api, &m)) {
+ IsDiscoverable(public_only, hiddenapi_context, &m)) {
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
DCHECK(!Runtime::Current()->IsActiveTransaction());
ObjPtr<mirror::Method> method =
@@ -819,7 +811,7 @@
soa.Self(),
ScopedNullHandle<mirror::ObjectArray<mirror::Class>>(),
kRuntimePointerSize);
- if (UNLIKELY(constructor == nullptr) || ShouldBlockAccessToMember(constructor, soa.Self())) {
+ if (UNLIKELY(constructor == nullptr) || ShouldDenyAccessToMember(constructor, soa.Self())) {
soa.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;",
"%s has no zero argument constructor",
klass->PrettyClass().c_str());
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 3dfa0c4..ccbc2d9 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -269,10 +269,8 @@
oat_file_manager_(nullptr),
is_low_memory_mode_(false),
safe_mode_(false),
- hidden_api_policy_(hiddenapi::EnforcementPolicy::kNoChecks),
- pending_hidden_api_warning_(false),
+ hidden_api_policy_(hiddenapi::EnforcementPolicy::kDisabled),
dedupe_hidden_api_warnings_(true),
- always_set_hidden_api_warning_flag_(false),
hidden_api_access_event_log_rate_(0),
dump_native_stack_on_sig_quit_(true),
pruned_dalvik_cache_(false),
@@ -1235,8 +1233,8 @@
// As is, we're encoding some logic here about which specific policy to use, which would be better
// controlled by the framework.
hidden_api_policy_ = do_hidden_api_checks
- ? hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList
- : hiddenapi::EnforcementPolicy::kNoChecks;
+ ? hiddenapi::EnforcementPolicy::kEnabled
+ : hiddenapi::EnforcementPolicy::kDisabled;
no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain);
force_native_bridge_ = runtime_options.Exists(Opt::ForceNativeBridge);
diff --git a/runtime/runtime.h b/runtime/runtime.h
index ad4d3bb..f6a5634 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -535,10 +535,6 @@
return hidden_api_policy_;
}
- void SetPendingHiddenApiWarning(bool value) {
- pending_hidden_api_warning_ = value;
- }
-
void SetHiddenApiExemptions(const std::vector<std::string>& exemptions) {
hidden_api_exemptions_ = exemptions;
}
@@ -547,10 +543,6 @@
return hidden_api_exemptions_;
}
- bool HasPendingHiddenApiWarning() const {
- return pending_hidden_api_warning_;
- }
-
void SetDedupeHiddenApiWarnings(bool value) {
dedupe_hidden_api_warnings_ = value;
}
@@ -559,14 +551,6 @@
return dedupe_hidden_api_warnings_;
}
- void AlwaysSetHiddenApiWarningFlag() {
- always_set_hidden_api_warning_flag_ = true;
- }
-
- bool ShouldAlwaysSetHiddenApiWarningFlag() const {
- return always_set_hidden_api_warning_flag_;
- }
-
void SetHiddenApiEventLogSampleRate(uint32_t rate) {
hidden_api_access_event_log_rate_ = rate;
}
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 65039bc..5300e2d 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -119,6 +119,7 @@
jfieldID WellKnownClasses::dalvik_system_DexFile_cookie;
jfieldID WellKnownClasses::dalvik_system_DexFile_fileName;
jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList;
+jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements;
jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
jfieldID WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
@@ -293,7 +294,7 @@
void WellKnownClasses::Init(JNIEnv* env) {
hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption(
- hiddenapi::EnforcementPolicy::kNoChecks);
+ hiddenapi::EnforcementPolicy::kDisabled);
dalvik_annotation_optimization_CriticalNative =
CacheClass(env, "dalvik/annotation/optimization/CriticalNative");
@@ -367,6 +368,7 @@
org_apache_harmony_dalvik_ddmc_DdmServer_dispatch = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
dalvik_system_BaseDexClassLoader_pathList = CacheField(env, dalvik_system_BaseDexClassLoader, false, "pathList", "Ldalvik/system/DexPathList;");
+ dalvik_system_BaseDexClassLoader_sharedLibraryLoaders = CacheField(env, dalvik_system_BaseDexClassLoader, false, "sharedLibraryLoaders", "[Ljava/lang/ClassLoader;");
dalvik_system_DexFile_cookie = CacheField(env, dalvik_system_DexFile, false, "mCookie", "Ljava/lang/Object;");
dalvik_system_DexFile_fileName = CacheField(env, dalvik_system_DexFile, false, "mFileName", "Ljava/lang/String;");
dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 130747c..0b7ed09 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -126,6 +126,7 @@
static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
static jfieldID dalvik_system_BaseDexClassLoader_pathList;
+ static jfieldID dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
static jfieldID dalvik_system_DexFile_cookie;
static jfieldID dalvik_system_DexFile_fileName;
static jfieldID dalvik_system_DexPathList_dexElements;
diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc
index 96754c3..8e3e4eb 100644
--- a/test/674-hiddenapi/hiddenapi.cc
+++ b/test/674-hiddenapi/hiddenapi.cc
@@ -28,9 +28,9 @@
extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) {
Runtime* runtime = Runtime::Current();
- runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly);
+ runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+ runtime->SetTargetSdkVersion(static_cast<int32_t>(hiddenapi::detail::SdkCodes::kVersionO_MR1));
runtime->SetDedupeHiddenApiWarnings(false);
- runtime->AlwaysSetHiddenApiWarningFlag();
}
extern "C" JNIEXPORT void JNICALL Java_Main_appendToBootClassLoader(
@@ -287,13 +287,5 @@
return static_cast<jint>(kAccHiddenApiBits);
}
-extern "C" JNIEXPORT jboolean JNICALL Java_ChildClass_hasPendingWarning(JNIEnv*, jclass) {
- return Runtime::Current()->HasPendingHiddenApiWarning();
-}
-
-extern "C" JNIEXPORT void JNICALL Java_ChildClass_clearWarning(JNIEnv*, jclass) {
- Runtime::Current()->SetPendingHiddenApiWarning(false);
-}
-
} // namespace Test674HiddenApi
} // namespace art
diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java
index db3ba6d..3427b8e 100644
--- a/test/674-hiddenapi/src-ex/ChildClass.java
+++ b/test/674-hiddenapi/src-ex/ChildClass.java
@@ -98,10 +98,8 @@
expected = Behaviour.Granted;
} else if (hiddenness == Hiddenness.Blacklist) {
expected = Behaviour.Denied;
- } else if (isDebuggable) {
- expected = Behaviour.Warning;
} else {
- expected = Behaviour.Granted;
+ expected = Behaviour.Warning;
}
for (boolean isStatic : booleanValues) {
@@ -145,7 +143,7 @@
}
private static void checkMemberCallback(Class<?> klass, String name,
- boolean isPublic, boolean isField) {
+ boolean isPublic, boolean isField, boolean expectedCallback) {
try {
RecordingConsumer consumer = new RecordingConsumer();
VMRuntime.setNonSdkApiUsageConsumer(consumer);
@@ -168,8 +166,14 @@
// only interested in whether the callback is invoked.
}
- if (consumer.recordedValue == null || !consumer.recordedValue.contains(name)) {
- throw new RuntimeException("No callback for member: " + name);
+ boolean actualCallback = consumer.recordedValue != null &&
+ consumer.recordedValue.contains(name);
+ if (expectedCallback != actualCallback) {
+ if (expectedCallback) {
+ throw new RuntimeException("Expected callback for member: " + name);
+ } else {
+ throw new RuntimeException("Did not expect callback for member: " + name);
+ }
}
} finally {
VMRuntime.setNonSdkApiUsageConsumer(null);
@@ -181,7 +185,7 @@
boolean isPublic = (visibility == Visibility.Public);
boolean canDiscover = (behaviour != Behaviour.Denied);
- boolean setsWarning = (behaviour == Behaviour.Warning);
+ boolean invokesMemberCallback = (behaviour != Behaviour.Granted);
if (klass.isInterface() && (!isStatic || !isPublic)) {
// Interfaces only have public static fields.
@@ -243,8 +247,6 @@
canDiscover);
}
- // Finish here if we could not discover the field.
-
if (canDiscover) {
// Test that modifiers are unaffected.
@@ -254,44 +256,22 @@
// Test getters and setters when meaningful.
- clearWarning();
if (!Reflection.canGetField(klass, name)) {
throwAccessException(klass, name, true, "Field.getInt()");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, true, "Field.getInt()", setsWarning);
- }
-
- clearWarning();
if (!Reflection.canSetField(klass, name)) {
throwAccessException(klass, name, true, "Field.setInt()");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, true, "Field.setInt()", setsWarning);
- }
-
- clearWarning();
if (!JNI.canGetField(klass, name, isStatic)) {
throwAccessException(klass, name, true, "getIntField");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, true, "getIntField", setsWarning);
- }
-
- clearWarning();
if (!JNI.canSetField(klass, name, isStatic)) {
throwAccessException(klass, name, true, "setIntField");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, true, "setIntField", setsWarning);
- }
}
// Test that callbacks are invoked correctly.
- clearWarning();
- if (setsWarning || !canDiscover) {
- checkMemberCallback(klass, name, isPublic, true /* isField */);
- }
+ checkMemberCallback(klass, name, isPublic, true /* isField */, invokesMemberCallback);
}
private static void checkMethod(Class<?> klass, String name, boolean isStatic,
@@ -304,7 +284,7 @@
}
boolean canDiscover = (behaviour != Behaviour.Denied);
- boolean setsWarning = (behaviour == Behaviour.Warning);
+ boolean invokesMemberCallback = (behaviour != Behaviour.Granted);
// Test discovery with reflection.
@@ -354,39 +334,21 @@
}
// Test whether we can invoke the method. This skips non-static interface methods.
-
if (!klass.isInterface() || isStatic) {
- clearWarning();
if (!Reflection.canInvokeMethod(klass, name)) {
throwAccessException(klass, name, false, "invoke()");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, false, "invoke()", setsWarning);
- }
-
- clearWarning();
if (!JNI.canInvokeMethodA(klass, name, isStatic)) {
throwAccessException(klass, name, false, "CallMethodA");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, false, "CallMethodA()", setsWarning);
- }
-
- clearWarning();
if (!JNI.canInvokeMethodV(klass, name, isStatic)) {
throwAccessException(klass, name, false, "CallMethodV");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, false, "CallMethodV()", setsWarning);
- }
}
}
// Test that callbacks are invoked correctly.
- clearWarning();
- if (setsWarning || !canDiscover) {
- checkMemberCallback(klass, name, isPublic, false /* isField */);
- }
+ checkMemberCallback(klass, name, isPublic, false /* isField */, invokesMemberCallback);
}
private static void checkConstructor(Class<?> klass, Visibility visibility, Hiddenness hiddenness,
@@ -403,7 +365,6 @@
MethodType methodType = MethodType.methodType(void.class, args);
boolean canDiscover = (behaviour != Behaviour.Denied);
- boolean setsWarning = (behaviour == Behaviour.Warning);
// Test discovery with reflection.
@@ -446,70 +407,41 @@
canDiscover);
}
- // Finish here if we could not discover the constructor.
+ if (canDiscover) {
+ // Test whether we can invoke the constructor.
- if (!canDiscover) {
- return;
- }
-
- // Test whether we can invoke the constructor.
-
- clearWarning();
- if (!Reflection.canInvokeConstructor(klass, args, initargs)) {
- throwAccessException(klass, fullName, false, "invoke()");
- }
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, fullName, false, "invoke()", setsWarning);
- }
-
- clearWarning();
- if (!JNI.canInvokeConstructorA(klass, signature)) {
- throwAccessException(klass, fullName, false, "NewObjectA");
- }
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, fullName, false, "NewObjectA", setsWarning);
- }
-
- clearWarning();
- if (!JNI.canInvokeConstructorV(klass, signature)) {
- throwAccessException(klass, fullName, false, "NewObjectV");
- }
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, fullName, false, "NewObjectV", setsWarning);
+ if (!Reflection.canInvokeConstructor(klass, args, initargs)) {
+ throwAccessException(klass, fullName, false, "invoke()");
+ }
+ if (!JNI.canInvokeConstructorA(klass, signature)) {
+ throwAccessException(klass, fullName, false, "NewObjectA");
+ }
+ if (!JNI.canInvokeConstructorV(klass, signature)) {
+ throwAccessException(klass, fullName, false, "NewObjectV");
+ }
}
}
private static void checkNullaryConstructor(Class<?> klass, Behaviour behaviour)
throws Exception {
boolean canAccess = (behaviour != Behaviour.Denied);
- boolean setsWarning = (behaviour == Behaviour.Warning);
- clearWarning();
if (Reflection.canUseNewInstance(klass) != canAccess) {
throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
"be able to construct " + klass.getName() + ". " +
"isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
}
- if (canAccess && hasPendingWarning() != setsWarning) {
- throwWarningException(klass, "nullary constructor", false, "newInstance", setsWarning);
- }
}
private static void checkLinking(String className, boolean takesParameter, Behaviour behaviour)
throws Exception {
boolean canAccess = (behaviour != Behaviour.Denied);
- boolean setsWarning = (behaviour == Behaviour.Warning);
- clearWarning();
if (Linking.canAccess(className, takesParameter) != canAccess) {
throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
"be able to verify " + className + "." +
"isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
}
- if (canAccess && hasPendingWarning() != setsWarning) {
- throwWarningException(
- Class.forName(className), "access", false, "static linking", setsWarning);
- }
}
private static void throwDiscoveryException(Class<?> klass, String name, boolean isField,
@@ -528,15 +460,6 @@
"everythingWhitelisted = " + everythingWhitelisted);
}
- private static void throwWarningException(Class<?> klass, String name, boolean isField,
- String fn, boolean setsWarning) {
- throw new RuntimeException("Expected access to " + (isField ? "field " : "method ") +
- klass.getName() + "." + name + " using " + fn + " to " + (setsWarning ? "" : "not ") +
- "set the warning flag. " +
- "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " +
- "everythingWhitelisted = " + everythingWhitelisted);
- }
-
private static void throwModifiersException(Class<?> klass, String name, boolean isField) {
throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() +
"." + name + " to not expose hidden modifiers");
@@ -545,7 +468,4 @@
private static boolean isParentInBoot;
private static boolean isChildInBoot;
private static boolean everythingWhitelisted;
-
- private static native boolean hasPendingWarning();
- private static native void clearWarning();
}
diff --git a/test/688-shared-library/expected.txt b/test/688-shared-library/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/688-shared-library/expected.txt
diff --git a/test/688-shared-library/info.txt b/test/688-shared-library/info.txt
new file mode 100644
index 0000000..2eda65d
--- /dev/null
+++ b/test/688-shared-library/info.txt
@@ -0,0 +1,2 @@
+Tests on BaseDexClassLoader shared libraries and their class
+loading behavior.
diff --git a/test/688-shared-library/src-art/Main.java b/test/688-shared-library/src-art/Main.java
new file mode 100644
index 0000000..d59e7dc
--- /dev/null
+++ b/test/688-shared-library/src-art/Main.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+import dalvik.system.DelegateLastClassLoader;
+import dalvik.system.PathClassLoader;
+
+public class Main {
+ static final String TEST_NAME = "688-shared-library";
+ static final String MAIN_JAR_FILE = System.getenv("DEX_LOCATION") + "/" + TEST_NAME + ".jar";
+ static final String EX_JAR_FILE = System.getenv("DEX_LOCATION") + "/" + TEST_NAME + "-ex.jar";
+ static ClassLoader bootLoader = Object.class.getClassLoader();
+
+ public static void main(String[] args) throws Exception {
+ testNoLibrary();
+ testOneLibrary();
+ testTwoLibraries1();
+ testTwoLibraries2();
+ testTransitive1();
+ testTransitive2();
+ testTransitive3();
+ testTransitive4();
+ }
+
+ public static void assertIdentical(Object expected, Object actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public static void testNoLibrary() throws Exception {
+ ClassLoader loader = new PathClassLoader(MAIN_JAR_FILE, null, bootLoader);
+ Class<?> cls = loader.loadClass("Main");
+ assertIdentical(loader, cls.getClassLoader());
+ }
+
+ public static void testOneLibrary() throws Exception {
+ ClassLoader[] sharedLibraries = {
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader delegateFirst =
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraries);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+
+ ClassLoader delegateLast =
+ new DelegateLastClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraries);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ }
+
+ public static void testTwoLibraries1() throws Exception {
+ ClassLoader[] sharedLibraries = {
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader),
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraries);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraries[1], cls.getClassLoader());
+
+ ClassLoader delegateLast =
+ new DelegateLastClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraries);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraries[1], cls.getClassLoader());
+ }
+
+ public static void testTwoLibraries2() throws Exception {
+ ClassLoader[] sharedLibraries = {
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader),
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraries);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+
+ ClassLoader delegateLast = new DelegateLastClassLoader("", null, bootLoader, sharedLibraries);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraries[0], cls.getClassLoader());
+ }
+
+ public static void testTransitive1() throws Exception {
+ ClassLoader[] sharedLibraryLevel2 = {
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader[] sharedLibraryLevel1 = {
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraryLevel2),
+ };
+
+ ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+
+ ClassLoader delegateLast =
+ new DelegateLastClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ }
+
+ public static void testTransitive2() throws Exception {
+ ClassLoader[] sharedLibraryLevel2 = {
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader[] sharedLibraryLevel1 = {
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader, sharedLibraryLevel2),
+ };
+
+ ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel1[0], cls.getClassLoader());
+
+ ClassLoader delegateLast =
+ new DelegateLastClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel1[0], cls.getClassLoader());
+ }
+
+ public static void testTransitive3() throws Exception {
+ ClassLoader[] sharedLibraryLevel2 = {
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader[] sharedLibraryLevel1 = {
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader, sharedLibraryLevel2),
+ sharedLibraryLevel2[0],
+ };
+
+ ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel1[0], cls.getClassLoader());
+
+ ClassLoader delegateLast =
+ new DelegateLastClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel1[0], cls.getClassLoader());
+ }
+
+ public static void testTransitive4() throws Exception {
+ ClassLoader[] sharedLibraryLevel2 = {
+ new PathClassLoader(EX_JAR_FILE, null, bootLoader),
+ };
+ ClassLoader[] sharedLibraryLevel1 = {
+ new PathClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraryLevel2),
+ sharedLibraryLevel2[0],
+ };
+
+ ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ Class<?> cls = delegateFirst.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateFirst.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+
+ ClassLoader delegateLast =
+ new DelegateLastClassLoader("", null, bootLoader, sharedLibraryLevel1);
+ cls = delegateLast.loadClass("Main");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ cls = delegateLast.loadClass("SharedLibraryOne");
+ assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+ }
+}
diff --git a/test/688-shared-library/src-ex/Main.java b/test/688-shared-library/src-ex/Main.java
new file mode 100644
index 0000000..f6555b9
--- /dev/null
+++ b/test/688-shared-library/src-ex/Main.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2018 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 Main {
+}
diff --git a/test/688-shared-library/src-ex/SharedLibraryOne.java b/test/688-shared-library/src-ex/SharedLibraryOne.java
new file mode 100644
index 0000000..d86755f
--- /dev/null
+++ b/test/688-shared-library/src-ex/SharedLibraryOne.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2018 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 SharedLibraryOne {
+}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index f0eacfa..ae20557 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1026,6 +1026,7 @@
"677-fsi2",
"678-quickening",
"679-locks",
+ "688-shared-library",
"999-redefine-hiddenapi",
"1000-non-moving-space-stress",
"1951-monitor-enter-no-suspend",
diff --git a/tools/build_linux_bionic_tests.sh b/tools/build_linux_bionic_tests.sh
index dba2d50..3ee7d50 100755
--- a/tools/build_linux_bionic_tests.sh
+++ b/tools/build_linux_bionic_tests.sh
@@ -79,6 +79,7 @@
$soong_out/bin/profmand
$soong_out/bin/hiddenapi
$soong_out/bin/hprof-conv
+ $soong_out/bin/timeout_dumper
$(find $ANDROID_HOST_OUT/lib64 -type f | sed "s:$ANDROID_HOST_OUT:$soong_out:g")
$(find $ANDROID_HOST_OUT/nativetest64 -type f | sed "s:$ANDROID_HOST_OUT:$soong_out:g"))
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index c1cc2e2..da463d5 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -61,7 +61,7 @@
done
# Allow to build successfully in master-art.
-extra_args=SOONG_ALLOW_MISSING_DEPENDENCIES=true
+extra_args="SOONG_ALLOW_MISSING_DEPENDENCIES=true TEMPORARY_DISABLE_PATH_RESTRICTIONS=true"
if [[ $mode == "host" ]]; then
make_command="make $j_arg $extra_args $showcommands build-art-host-tests $common_targets"
@@ -94,4 +94,5 @@
echo "Executing $make_command"
+# Disable path restrictions to enable luci builds using vpython.
bash -c "$make_command"