hiddenapi: Print warnings for @CorePlatformApi violations
When accessing a method/field at runtime, determine the context of both
the caller and the callee, and add new logic for the case
"platform -> core-platform" which used to be always allowed.
If the callee is marked with kAccCorePlatformApi, access is allowed.
If not, a warning is printed into logcat.
Bug: 119068555
Test: 674-hiddenapi
Change-Id: I64839596bf6eb06d7a169fd59b18fd82c140ce6e
diff --git a/libartbase/base/hiddenapi_domain.h b/libartbase/base/hiddenapi_domain.h
new file mode 100644
index 0000000..4cbc22d
--- /dev/null
+++ b/libartbase/base/hiddenapi_domain.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBARTBASE_BASE_HIDDENAPI_DOMAIN_H_
+#define ART_LIBARTBASE_BASE_HIDDENAPI_DOMAIN_H_
+
+namespace art {
+namespace hiddenapi {
+
+// List of domains supported by the hidden API access checks. Domain with a lower
+// ordinal is considered more "trusted", i.e. always allowed to access members of
+// domains with a greater ordinal. Access checks are performed when code tries to
+// access a method/field from a more trusted domain than itself.
+enum class Domain {
+ kCorePlatform = 0,
+ kPlatform,
+ kApplication,
+};
+
+inline bool IsDomainMoreTrustedThan(Domain domainA, Domain domainB) {
+ return static_cast<uint32_t>(domainA) <= static_cast<uint32_t>(domainB);
+}
+
+} // namespace hiddenapi
+} // namespace art
+
+
+#endif // ART_LIBARTBASE_BASE_HIDDENAPI_DOMAIN_H_
diff --git a/libdexfile/dex/art_dex_file_loader.cc b/libdexfile/dex/art_dex_file_loader.cc
index 57e838f..a814b66 100644
--- a/libdexfile/dex/art_dex_file_loader.cc
+++ b/libdexfile/dex/art_dex_file_loader.cc
@@ -547,7 +547,7 @@
// that this will call `realpath`.
std::string path = DexFileLoader::GetDexCanonicalLocation(location.c_str());
if (dex_file != nullptr && LocationIsOnSystemFramework(path.c_str())) {
- dex_file->SetIsPlatformDexFile();
+ dex_file->SetHiddenapiDomain(hiddenapi::Domain::kPlatform);
}
return dex_file;
diff --git a/libdexfile/dex/art_dex_file_loader_test.cc b/libdexfile/dex/art_dex_file_loader_test.cc
index f9516db..8c9258b 100644
--- a/libdexfile/dex/art_dex_file_loader_test.cc
+++ b/libdexfile/dex/art_dex_file_loader_test.cc
@@ -340,7 +340,7 @@
ASSERT_GE(dex_files.size(), 1u);
for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
- ASSERT_FALSE(dex_file->IsPlatformDexFile());
+ ASSERT_NE(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
}
dex_files.clear();
@@ -368,7 +368,7 @@
ASSERT_GE(dex_files.size(), 1u);
for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
- ASSERT_FALSE(dex_file->IsPlatformDexFile());
+ ASSERT_NE(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
}
dex_files.clear();
@@ -396,7 +396,7 @@
ASSERT_GE(dex_files.size(), 1u);
for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
- ASSERT_TRUE(dex_file->IsPlatformDexFile());
+ ASSERT_EQ(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
}
dex_files.clear();
@@ -424,7 +424,7 @@
ASSERT_GT(dex_files.size(), 1u);
for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
- ASSERT_FALSE(dex_file->IsPlatformDexFile());
+ ASSERT_NE(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
}
dex_files.clear();
@@ -453,7 +453,7 @@
ASSERT_GT(dex_files.size(), 1u);
for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
- ASSERT_FALSE(dex_file->IsPlatformDexFile());
+ ASSERT_NE(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
}
dex_files.clear();
@@ -482,7 +482,7 @@
ASSERT_GT(dex_files.size(), 1u);
for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
- ASSERT_TRUE(dex_file->IsPlatformDexFile());
+ ASSERT_EQ(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
}
dex_files.clear();
diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc
index d3cdf13..39377a3 100644
--- a/libdexfile/dex/dex_file.cc
+++ b/libdexfile/dex/dex_file.cc
@@ -123,7 +123,7 @@
oat_dex_file_(oat_dex_file),
container_(std::move(container)),
is_compact_dex_(is_compact_dex),
- is_platform_dex_(false) {
+ hiddenapi_domain_(hiddenapi::Domain::kApplication) {
CHECK(begin_ != nullptr) << GetLocation();
CHECK_GT(size_, 0U) << GetLocation();
// Check base (=header) alignment.
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index a940a66..c7fbe78 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -24,6 +24,7 @@
#include <android-base/logging.h>
#include "base/globals.h"
+#include "base/hiddenapi_domain.h"
#include "base/macros.h"
#include "base/value_object.h"
#include "class_iterator.h"
@@ -754,13 +755,8 @@
ALWAYS_INLINE const StandardDexFile* AsStandardDexFile() const;
ALWAYS_INLINE const CompactDexFile* AsCompactDexFile() const;
- ALWAYS_INLINE bool IsPlatformDexFile() const {
- return is_platform_dex_;
- }
-
- ALWAYS_INLINE void SetIsPlatformDexFile() {
- is_platform_dex_ = true;
- }
+ hiddenapi::Domain GetHiddenapiDomain() const { return hiddenapi_domain_; }
+ void SetHiddenapiDomain(hiddenapi::Domain value) { hiddenapi_domain_ = value; }
bool IsInMainSection(const void* addr) const {
return Begin() <= addr && addr < Begin() + Size();
@@ -874,8 +870,7 @@
// If the dex file is a compact dex file. If false then the dex file is a standard dex file.
const bool is_compact_dex_;
- // If the dex file is located in /system/framework/.
- bool is_platform_dex_;
+ hiddenapi::Domain hiddenapi_domain_;
friend class DexFileLoader;
friend class DexFileVerifierTest;
diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc
index 41e4291..a3e06e6 100644
--- a/openjdkjvmti/fixed_up_dex_file.cc
+++ b/openjdkjvmti/fixed_up_dex_file.cc
@@ -141,14 +141,12 @@
/*verify_checksum=*/false,
&error);
- if (new_dex_file == nullptr) {
+ if (new_dex_file == nullptr) {
LOG(ERROR) << "Unable to open dex file from memory for unquickening! error: " << error;
return nullptr;
}
- if (original.IsPlatformDexFile()) {
- const_cast<art::DexFile*>(new_dex_file.get())->SetIsPlatformDexFile();
- }
+ const_cast<art::DexFile*>(new_dex_file.get())->SetHiddenapiDomain(original.GetHiddenapiDomain());
DoDexUnquicken(*new_dex_file, original);
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index adcf6b3..af5e67a 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -67,6 +67,19 @@
return os;
}
+static inline std::ostream& operator<<(std::ostream& os, const AccessContext& value)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!value.GetClass().IsNull()) {
+ std::string tmp;
+ os << value.GetClass()->GetDescriptor(&tmp);
+ } else if (value.GetDexFile() != nullptr) {
+ os << value.GetDexFile()->GetLocation();
+ } else {
+ os << "<unknown_caller>";
+ }
+ return os;
+}
+
namespace detail {
// Do not change the values of items in this enum, as they are written to the
@@ -349,9 +362,18 @@
}
template<typename T>
-bool ShouldDenyAccessToMemberImpl(T* member,
- hiddenapi::ApiList api_list,
- AccessMethod access_method) {
+void MaybeReportCorePlatformApiViolation(T* member,
+ const AccessContext& caller_context,
+ AccessMethod access_method) {
+ if (access_method != AccessMethod::kNone) {
+ MemberSignature sig(member);
+ LOG(ERROR) << "CorePlatformApi violation: " << Dumpable<MemberSignature>(sig)
+ << " from " << caller_context << " using " << access_method;
+ }
+}
+
+template<typename T>
+bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method) {
DCHECK(member != nullptr);
Runtime* runtime = Runtime::Current();
@@ -406,14 +428,20 @@
return deny_access;
}
-// Need to instantiate this.
+// Need to instantiate these.
template uint32_t GetDexFlags<ArtField>(ArtField* member);
template uint32_t GetDexFlags<ArtMethod>(ArtMethod* member);
+template void MaybeReportCorePlatformApiViolation(ArtField* member,
+ const AccessContext& caller_context,
+ AccessMethod access_method);
+template void MaybeReportCorePlatformApiViolation(ArtMethod* member,
+ const AccessContext& caller_context,
+ AccessMethod access_method);
template bool ShouldDenyAccessToMemberImpl<ArtField>(ArtField* member,
- hiddenapi::ApiList api_list,
+ ApiList api_list,
AccessMethod access_method);
template bool ShouldDenyAccessToMemberImpl<ArtMethod>(ArtMethod* member,
- hiddenapi::ApiList api_list,
+ ApiList api_list,
AccessMethod access_method);
} // namespace detail
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index f085033..c73a710 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -52,50 +52,91 @@
kLinking,
};
-struct AccessContext {
+// Represents the API domain of a caller/callee.
+class AccessContext {
public:
- explicit AccessContext(bool is_trusted) : is_trusted_(is_trusted) {}
+ // Initialize to either the fully-trusted or fully-untrusted domain.
+ explicit AccessContext(bool is_trusted)
+ : klass_(nullptr),
+ dex_file_(nullptr),
+ domain_(ComputeDomain(is_trusted)) {}
- explicit AccessContext(ObjPtr<mirror::Class> klass) : is_trusted_(GetIsTrusted(klass)) {}
-
+ // Initialize from class loader and dex file (via dex cache).
AccessContext(ObjPtr<mirror::ClassLoader> class_loader, ObjPtr<mirror::DexCache> dex_cache)
- : is_trusted_(GetIsTrusted(class_loader, dex_cache)) {}
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ : klass_(nullptr),
+ dex_file_(GetDexFileFromDexCache(dex_cache)),
+ domain_(ComputeDomain(class_loader, dex_file_)) {}
- bool IsTrusted() const { return is_trusted_; }
+ // Initialize from Class.
+ explicit AccessContext(ObjPtr<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ : klass_(klass),
+ dex_file_(GetDexFileFromDexCache(klass->GetDexCache())),
+ domain_(ComputeDomain(klass, dex_file_)) {}
+
+ ObjPtr<mirror::Class> GetClass() const { return klass_; }
+ const DexFile* GetDexFile() const { return dex_file_; }
+ Domain GetDomain() const { return domain_; }
+
+ bool IsUntrustedDomain() const { return domain_ == Domain::kApplication; }
+
+ // Returns true if this domain is always allowed to access the domain of `callee`.
+ bool CanAlwaysAccess(const AccessContext& callee) const {
+ return IsDomainMoreTrustedThan(domain_, callee.domain_);
+ }
private:
- static bool GetIsTrusted(ObjPtr<mirror::ClassLoader> class_loader,
- ObjPtr<mirror::DexCache> dex_cache)
+ static const DexFile* GetDexFileFromDexCache(ObjPtr<mirror::DexCache> dex_cache)
REQUIRES_SHARED(Locks::mutator_lock_) {
- // Trust if the caller is in is boot class loader.
- if (class_loader.IsNull()) {
- return true;
- }
-
- // Trust if caller is in a platform dex file.
- if (!dex_cache.IsNull()) {
- const DexFile* dex_file = dex_cache->GetDexFile();
- if (dex_file != nullptr && dex_file->IsPlatformDexFile()) {
- return true;
- }
- }
-
- return false;
+ return dex_cache.IsNull() ? nullptr : dex_cache->GetDexFile();
}
- static bool GetIsTrusted(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK(!klass.IsNull());
+ static Domain ComputeDomain(bool is_trusted) {
+ return is_trusted ? Domain::kCorePlatform : Domain::kApplication;
+ }
- if (klass->ShouldSkipHiddenApiChecks() && Runtime::Current()->IsJavaDebuggable()) {
- // Class is known, it is marked trusted and we are in debuggable mode.
- return true;
+ static Domain ComputeDomain(ObjPtr<mirror::ClassLoader> class_loader, const DexFile* dex_file)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (dex_file == nullptr) {
+ return ComputeDomain(/* is_trusted= */ class_loader.IsNull());
}
+ Domain dex_domain = dex_file->GetHiddenapiDomain();
+ if (class_loader.IsNull() && dex_domain == Domain::kApplication) {
+ LOG(WARNING) << "DexFile " << dex_file->GetLocation() << " is in boot classpath "
+ << "but is assigned untrusted domain";
+ dex_domain = Domain::kPlatform;
+ }
+ return dex_domain;
+ }
+
+ static Domain ComputeDomain(ObjPtr<mirror::Class> klass, const DexFile* dex_file)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
// Check other aspects of the context.
- return GetIsTrusted(klass->GetClassLoader(), klass->GetDexCache());
+ Domain domain = ComputeDomain(klass->GetClassLoader(), dex_file);
+
+ if (domain == Domain::kApplication &&
+ klass->ShouldSkipHiddenApiChecks() &&
+ Runtime::Current()->IsJavaDebuggable()) {
+ // Class is known, it is marked trusted and we are in debuggable mode.
+ domain = ComputeDomain(/* is_trusted= */ true);
+ }
+
+ return domain;
}
- bool is_trusted_;
+ // Pointer to declaring class of the caller/callee (null if not provided).
+ // This is not safe across GC but we're only using this class for passing
+ // information about the caller to the access check logic and never retain
+ // the AccessContext instance beyond that.
+ const ObjPtr<mirror::Class> klass_;
+
+ // DexFile of the caller/callee (null if not provided).
+ const DexFile* const dex_file_;
+
+ // Computed domain of the caller/callee.
+ const Domain domain_;
};
class ScopedHiddenApiEnforcementPolicySetting {
@@ -169,6 +210,12 @@
uint32_t GetDexFlags(T* member) REQUIRES_SHARED(Locks::mutator_lock_);
template<typename T>
+void MaybeReportCorePlatformApiViolation(T* member,
+ const AccessContext& caller_context,
+ AccessMethod access_method)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+template<typename T>
bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -309,45 +356,86 @@
AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(member != nullptr);
+ const uint32_t runtime_flags = GetRuntimeFlags(member);
// Exit early if member is public API. This flag is also set for non-boot class
// path fields/methods.
- if ((GetRuntimeFlags(member) & kAccPublicApi) != 0) {
+ if ((runtime_flags & kAccPublicApi) != 0) {
return false;
}
- // Exit early if access checks are completely disabled.
- if (Runtime::Current()->GetHiddenApiEnforcementPolicy() == EnforcementPolicy::kDisabled) {
+ // Determine which domain the caller and callee belong to.
+ // This can be *very* expensive. This is why ShouldDenyAccessToMember
+ // should not be called on every individual access.
+ const AccessContext caller_context = fn_get_access_context();
+ const AccessContext callee_context(member->GetDeclaringClass());
+
+ // Non-boot classpath callers should have exited early.
+ DCHECK(!callee_context.IsUntrustedDomain());
+
+ // Check if the caller is always allowed to access members in the callee context.
+ if (caller_context.CanAlwaysAccess(callee_context)) {
return false;
}
- // Check if caller is exempted from access checks.
- // This can be *very* expensive. Save it for last.
- if (fn_get_access_context().IsTrusted()) {
- return false;
+ // Check if this is platform accessing core platform. We may warn if `member` is
+ // not part of core platform API.
+ switch (caller_context.GetDomain()) {
+ case Domain::kApplication: {
+ DCHECK(!callee_context.IsUntrustedDomain());
+
+ // Exit early if access checks are completely disabled.
+ EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
+ if (policy == EnforcementPolicy::kDisabled) {
+ return false;
+ }
+
+ // Decode hidden API access flags from the dex file.
+ // This is an O(N) operation scaling with the number of fields/methods
+ // in the class. Only do this on slow path and only do it once.
+ ApiList api_list(detail::GetDexFlags(member));
+ DCHECK(api_list.IsValid());
+
+ // Member is hidden and caller is not exempted. Enter slow path.
+ return detail::ShouldDenyAccessToMemberImpl(member, api_list, access_method);
+ }
+
+ case Domain::kPlatform: {
+ DCHECK(callee_context.GetDomain() == Domain::kCorePlatform);
+
+ // Member is part of core platform API. Accessing it is allowed.
+ if ((runtime_flags & kAccCorePlatformApi) != 0) {
+ return false;
+ }
+
+ // Allow access if access checks are disabled.
+ EnforcementPolicy policy = Runtime::Current()->GetCorePlatformApiEnforcementPolicy();
+ if (policy == EnforcementPolicy::kDisabled) {
+ return false;
+ }
+
+ // Access checks are not disabled, report the violation.
+ detail::MaybeReportCorePlatformApiViolation(member, caller_context, access_method);
+
+ // Deny access if the policy is enabled.
+ return policy == EnforcementPolicy::kEnabled;
+ }
+
+ case Domain::kCorePlatform: {
+ LOG(FATAL) << "CorePlatform domain should be allowed to access all domains";
+ UNREACHABLE();
+ }
}
-
- // Decode hidden API access flags from the dex file.
- // This is an O(N) operation scaling with the number of fields/methods
- // in the class. Only do this on slow path and only do it once.
- ApiList api_list(detail::GetDexFlags(member));
- DCHECK(api_list.IsValid());
-
- // Member is hidden and caller is not exempted. Enter slow path.
- return detail::ShouldDenyAccessToMemberImpl(member, api_list, access_method);
}
// Helper method for callers where access context can be determined beforehand.
// Wraps AccessContext in a lambda and passes it to the real ShouldDenyAccessToMember.
template<typename T>
inline bool ShouldDenyAccessToMember(T* member,
- AccessContext access_context,
+ const AccessContext& access_context,
AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return ShouldDenyAccessToMember(
- member,
- [&] () REQUIRES_SHARED(Locks::mutator_lock_) { return access_context; },
- access_method);
+ return ShouldDenyAccessToMember(member, [&]() { return access_context; }, access_method);
}
} // namespace hiddenapi
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 4fa7271..72a8727 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -185,13 +185,13 @@
static ALWAYS_INLINE bool ShouldDenyAccessToMember(T* member, ShadowFrame* frame)
REQUIRES_SHARED(Locks::mutator_lock_) {
// All uses in this file are from reflection
- constexpr hiddenapi::AccessMethod access_method = hiddenapi::AccessMethod::kReflection;
+ constexpr hiddenapi::AccessMethod kAccessMethod = hiddenapi::AccessMethod::kReflection;
return hiddenapi::ShouldDenyAccessToMember(
member,
[&]() REQUIRES_SHARED(Locks::mutator_lock_) {
return hiddenapi::AccessContext(frame->GetMethod()->GetDeclaringClass());
},
- access_method);
+ kAccessMethod);
}
void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self,
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 52482b7..daf02fe 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -836,8 +836,9 @@
return;
}
+ // Assign core platform domain as the dex files are allowed to access all the other domains.
for (const DexFile* dex_file : dex_files) {
- const_cast<DexFile*>(dex_file)->SetIsPlatformDexFile();
+ const_cast<DexFile*>(dex_file)->SetHiddenapiDomain(hiddenapi::Domain::kCorePlatform);
}
}
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index d022c3b..4115791 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -130,7 +130,7 @@
// callers (hiddenapi_context).
template<typename T>
ALWAYS_INLINE static bool IsDiscoverable(bool public_only,
- hiddenapi::AccessContext access_context,
+ const hiddenapi::AccessContext& access_context,
T* member)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (public_only && ((member->GetAccessFlags() & kAccPublic) == 0)) {
@@ -508,8 +508,9 @@
}
static ALWAYS_INLINE inline bool MethodMatchesConstructor(
- ArtMethod* m, bool public_only, hiddenapi::AccessContext hiddenapi_context)
- REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtMethod* m,
+ bool public_only,
+ const hiddenapi::AccessContext& hiddenapi_context) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(m != nullptr);
return m->IsConstructor() &&
!m->IsStatic() &&
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index d537de5..4853187 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -273,6 +273,7 @@
is_low_memory_mode_(false),
safe_mode_(false),
hidden_api_policy_(hiddenapi::EnforcementPolicy::kDisabled),
+ core_platform_api_policy_(hiddenapi::EnforcementPolicy::kJustWarn),
dedupe_hidden_api_warnings_(true),
hidden_api_access_event_log_rate_(0),
dump_native_stack_on_sig_quit_(true),
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 1d1d0d3..ee2c514 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -539,6 +539,14 @@
return hidden_api_policy_;
}
+ void SetCorePlatformApiEnforcementPolicy(hiddenapi::EnforcementPolicy policy) {
+ core_platform_api_policy_ = policy;
+ }
+
+ hiddenapi::EnforcementPolicy GetCorePlatformApiEnforcementPolicy() const {
+ return core_platform_api_policy_;
+ }
+
void SetHiddenApiExemptions(const std::vector<std::string>& exemptions) {
hidden_api_exemptions_ = exemptions;
}
@@ -1075,23 +1083,17 @@
// Whether access checks on hidden API should be performed.
hiddenapi::EnforcementPolicy hidden_api_policy_;
+ // Whether access checks on core platform API should be performed.
+ hiddenapi::EnforcementPolicy core_platform_api_policy_;
+
// List of signature prefixes of methods that have been removed from the blacklist, and treated
// as if whitelisted.
std::vector<std::string> hidden_api_exemptions_;
- // Whether the application has used an API which is not restricted but we
- // should issue a warning about it.
- bool pending_hidden_api_warning_;
-
// Do not warn about the same hidden API access violation twice.
// This is only used for testing.
bool dedupe_hidden_api_warnings_;
- // Hidden API can print warnings into the log and/or set a flag read by the
- // framework to show a UI warning. If this flag is set, always set the flag
- // when there is a warning. This is only used for testing.
- bool always_set_hidden_api_warning_flag_;
-
// How often to log hidden API access to the event log. An integer between 0
// (never) and 0x10000 (always).
uint32_t hidden_api_access_event_log_rate_;
diff --git a/test/674-hiddenapi/hiddenapi-flags.csv b/test/674-hiddenapi/hiddenapi-flags.csv
index d875bdf..42626f7 100644
--- a/test/674-hiddenapi/hiddenapi-flags.csv
+++ b/test/674-hiddenapi/hiddenapi-flags.csv
@@ -1,30 +1,41 @@
+LNullaryConstructorBlacklistAndCorePlatformApi;-><init>()V,blacklist,core-platform-api
LNullaryConstructorBlacklist;-><init>()V,blacklist
LNullaryConstructorDarkGreylist;-><init>()V,greylist-max-o
LNullaryConstructorLightGreylist;-><init>()V,greylist
+LParentClass;->fieldPackageBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
LParentClass;->fieldPackageBlacklist:I,blacklist
LParentClass;->fieldPackageDarkGreylist:I,greylist-max-o
LParentClass;->fieldPackageLightGreylist:I,greylist
+LParentClass;->fieldPackageStaticBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
LParentClass;->fieldPackageStaticBlacklist:I,blacklist
LParentClass;->fieldPackageStaticDarkGreylist:I,greylist-max-o
LParentClass;->fieldPackageStaticLightGreylist:I,greylist
+LParentClass;->fieldPrivateBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
LParentClass;->fieldPrivateBlacklist:I,blacklist
LParentClass;->fieldPrivateDarkGreylist:I,greylist-max-o
LParentClass;->fieldPrivateLightGreylist:I,greylist
+LParentClass;->fieldPrivateStaticBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
LParentClass;->fieldPrivateStaticBlacklist:I,blacklist
LParentClass;->fieldPrivateStaticDarkGreylist:I,greylist-max-o
LParentClass;->fieldPrivateStaticLightGreylist:I,greylist
+LParentClass;->fieldProtectedBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
LParentClass;->fieldProtectedBlacklist:I,blacklist
LParentClass;->fieldProtectedDarkGreylist:I,greylist-max-o
LParentClass;->fieldProtectedLightGreylist:I,greylist
+LParentClass;->fieldProtectedStaticBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
LParentClass;->fieldProtectedStaticBlacklist:I,blacklist
LParentClass;->fieldProtectedStaticDarkGreylist:I,greylist-max-o
LParentClass;->fieldProtectedStaticLightGreylist:I,greylist
+LParentClass;->fieldPublicBlacklistAndCorePlatformApiB:I,blacklist,core-platform-api
+LParentClass;->fieldPublicBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
LParentClass;->fieldPublicBlacklistB:I,blacklist
LParentClass;->fieldPublicBlacklist:I,blacklist
LParentClass;->fieldPublicDarkGreylistB:I,greylist-max-o
LParentClass;->fieldPublicDarkGreylist:I,greylist-max-o
LParentClass;->fieldPublicLightGreylistB:I,greylist
LParentClass;->fieldPublicLightGreylist:I,greylist
+LParentClass;->fieldPublicStaticBlacklistAndCorePlatformApiB:I,blacklist,core-platform-api
+LParentClass;->fieldPublicStaticBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
LParentClass;->fieldPublicStaticBlacklistB:I,blacklist
LParentClass;->fieldPublicStaticBlacklist:I,blacklist
LParentClass;->fieldPublicStaticDarkGreylistB:I,greylist-max-o
@@ -33,49 +44,65 @@
LParentClass;->fieldPublicStaticLightGreylist:I,greylist
LParentClass;-><init>(DB)V,greylist-max-o
LParentClass;-><init>(DC)V,blacklist
+LParentClass;-><init>(DI)V,blacklist,core-platform-api
LParentClass;-><init>(DZ)V,greylist
LParentClass;-><init>(FB)V,greylist-max-o
LParentClass;-><init>(FC)V,blacklist
+LParentClass;-><init>(FI)V,blacklist,core-platform-api
LParentClass;-><init>(FZ)V,greylist
LParentClass;-><init>(IB)V,greylist-max-o
LParentClass;-><init>(IC)V,blacklist
+LParentClass;-><init>(II)V,blacklist,core-platform-api
LParentClass;-><init>(IZ)V,greylist
LParentClass;-><init>(JB)V,greylist-max-o
LParentClass;-><init>(JC)V,blacklist
+LParentClass;-><init>(JI)V,blacklist,core-platform-api
LParentClass;-><init>(JZ)V,greylist
+LParentClass;->methodPackageBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentClass;->methodPackageBlacklist()I,blacklist
LParentClass;->methodPackageDarkGreylist()I,greylist-max-o
LParentClass;->methodPackageLightGreylist()I,greylist
+LParentClass;->methodPackageStaticBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentClass;->methodPackageStaticBlacklist()I,blacklist
LParentClass;->methodPackageStaticDarkGreylist()I,greylist-max-o
LParentClass;->methodPackageStaticLightGreylist()I,greylist
+LParentClass;->methodPrivateBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentClass;->methodPrivateBlacklist()I,blacklist
LParentClass;->methodPrivateDarkGreylist()I,greylist-max-o
LParentClass;->methodPrivateLightGreylist()I,greylist
+LParentClass;->methodPrivateStaticBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentClass;->methodPrivateStaticBlacklist()I,blacklist
LParentClass;->methodPrivateStaticDarkGreylist()I,greylist-max-o
LParentClass;->methodPrivateStaticLightGreylist()I,greylist
+LParentClass;->methodProtectedBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentClass;->methodProtectedBlacklist()I,blacklist
LParentClass;->methodProtectedDarkGreylist()I,greylist-max-o
LParentClass;->methodProtectedLightGreylist()I,greylist
+LParentClass;->methodProtectedStaticBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentClass;->methodProtectedStaticBlacklist()I,blacklist
LParentClass;->methodProtectedStaticDarkGreylist()I,greylist-max-o
LParentClass;->methodProtectedStaticLightGreylist()I,greylist
+LParentClass;->methodPublicBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentClass;->methodPublicBlacklist()I,blacklist
LParentClass;->methodPublicDarkGreylist()I,greylist-max-o
LParentClass;->methodPublicLightGreylist()I,greylist
+LParentClass;->methodPublicStaticBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentClass;->methodPublicStaticBlacklist()I,blacklist
LParentClass;->methodPublicStaticDarkGreylist()I,greylist-max-o
LParentClass;->methodPublicStaticLightGreylist()I,greylist
+LParentInterface;->fieldPublicStaticBlacklistAndCorePlatformApi:I,blacklist,core-platform-api
LParentInterface;->fieldPublicStaticBlacklist:I,blacklist
LParentInterface;->fieldPublicStaticDarkGreylist:I,greylist-max-o
LParentInterface;->fieldPublicStaticLightGreylist:I,greylist
+LParentInterface;->methodPublicBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentInterface;->methodPublicBlacklist()I,blacklist
LParentInterface;->methodPublicDarkGreylist()I,greylist-max-o
+LParentInterface;->methodPublicDefaultBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentInterface;->methodPublicDefaultBlacklist()I,blacklist
LParentInterface;->methodPublicDefaultDarkGreylist()I,greylist-max-o
LParentInterface;->methodPublicDefaultLightGreylist()I,greylist
LParentInterface;->methodPublicLightGreylist()I,greylist
+LParentInterface;->methodPublicStaticBlacklistAndCorePlatformApi()I,blacklist,core-platform-api
LParentInterface;->methodPublicStaticBlacklist()I,blacklist
LParentInterface;->methodPublicStaticDarkGreylist()I,greylist-max-o
LParentInterface;->methodPublicStaticLightGreylist()I,greylist
diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc
index 2464263..8dfb402 100644
--- a/test/674-hiddenapi/hiddenapi.cc
+++ b/test/674-hiddenapi/hiddenapi.cc
@@ -27,40 +27,59 @@
namespace art {
namespace Test674HiddenApi {
+std::vector<std::vector<std::unique_ptr<const DexFile>>> opened_dex_files;
+
extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) {
Runtime* runtime = Runtime::Current();
runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+ runtime->SetCorePlatformApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
runtime->SetTargetSdkVersion(
static_cast<uint32_t>(hiddenapi::ApiList::GreylistMaxO().GetMaxAllowedSdkVersion()));
runtime->SetDedupeHiddenApiWarnings(false);
}
-extern "C" JNIEXPORT void JNICALL Java_Main_appendToBootClassLoader(
- JNIEnv* env, jclass, jstring jpath) {
+extern "C" JNIEXPORT void JNICALL Java_Main_setDexDomain(
+ JNIEnv*, jclass, jint int_index, jboolean is_core_platform) {
+ size_t index = static_cast<size_t>(int_index);
+ CHECK_LT(index, opened_dex_files.size());
+ for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files[index]) {
+ const_cast<DexFile*>(dex_file.get())->SetHiddenapiDomain(
+ (is_core_platform == JNI_FALSE) ? hiddenapi::Domain::kPlatform
+ : hiddenapi::Domain::kCorePlatform);
+ }
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_appendToBootClassLoader(
+ JNIEnv* env, jclass klass, jstring jpath, jboolean is_core_platform) {
ScopedUtfChars utf(env, jpath);
const char* path = utf.c_str();
- if (path == nullptr) {
- return;
- }
+ CHECK(path != nullptr);
+
+ const size_t index = opened_dex_files.size();
+ const jint int_index = static_cast<jint>(index);
+ opened_dex_files.push_back(std::vector<std::unique_ptr<const DexFile>>());
ArtDexFileLoader dex_loader;
std::string error_msg;
- std::vector<std::unique_ptr<const DexFile>> dex_files;
+
if (!dex_loader.Open(path,
path,
/* verify */ false,
/* verify_checksum */ true,
&error_msg,
- &dex_files)) {
+ &opened_dex_files[index])) {
LOG(FATAL) << "Could not open " << path << " for boot classpath extension: " << error_msg;
UNREACHABLE();
}
+ Java_Main_setDexDomain(env, klass, int_index, is_core_platform);
+
ScopedObjectAccess soa(Thread::Current());
- for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
- Runtime::Current()->GetClassLinker()->AppendToBootClassPath(
- Thread::Current(), *dex_file.release());
+ for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files[index]) {
+ Runtime::Current()->GetClassLinker()->AppendToBootClassPath(Thread::Current(), *dex_file.get());
}
+
+ return int_index;
}
static jobject NewInstance(JNIEnv* env, jclass klass) {
diff --git a/test/674-hiddenapi/run b/test/674-hiddenapi/run
new file mode 100755
index 0000000..2babeef
--- /dev/null
+++ b/test/674-hiddenapi/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Make verification soft fail so that we can re-verify boot classpath
+# methods at runtime.
+exec ${RUN} $@ --verify-soft-fail
\ No newline at end of file
diff --git a/test/674-hiddenapi/src-art/Main.java b/test/674-hiddenapi/src-art/Main.java
index 782748c..190f4ac 100644
--- a/test/674-hiddenapi/src-art/Main.java
+++ b/test/674-hiddenapi/src-art/Main.java
@@ -28,6 +28,13 @@
import java.util.zip.ZipFile;
public class Main {
+ // This needs to be kept in sync with DexDomain in ChildClass.
+ enum DexDomain {
+ CorePlatform,
+ Platform,
+ Application
+ }
+
public static void main(String[] args) throws Exception {
System.loadLibrary(args[0]);
prepareNativeLibFileName(args[0]);
@@ -40,70 +47,88 @@
// or test the wrong thing. We rely on not deduping hidden API warnings
// here for the same reasons), meaning the code under test and production
// code are running in different configurations. Each test should be run in
- // a fresh process to ensure that they are working correcting and not
- // accidentally interfering with eachother.
+ // a fresh process to ensure that they are working correctly and not
+ // accidentally interfering with each other.
+ // As a side effect, we also cannot test Platform->Platform and later
+ // Platform->CorePlatform as the former succeeds in verifying linkage usages
+ // that should fail in the latter.
// Run test with both parent and child dex files loaded with class loaders.
// The expectation is that hidden members in parent should be visible to
// the child.
- doTest(false, false, false);
+ doTest(DexDomain.Application, DexDomain.Application, false);
doUnloading();
// Now append parent dex file to boot class path and run again. This time
// the child dex file should not be able to access private APIs of the
// parent.
- appendToBootClassLoader(DEX_PARENT_BOOT);
- doTest(true, false, false);
+ int parentIdx = appendToBootClassLoader(DEX_PARENT_BOOT, /* isCorePlatform */ false);
+ doTest(DexDomain.Platform, DexDomain.Application, false);
doUnloading();
// Now run the same test again, but with the blacklist exmemptions list set
// to "L" which matches everything.
- doTest(true, false, true);
+ doTest(DexDomain.Platform, DexDomain.Application, true);
doUnloading();
- // And finally append to child to boot class path as well. With both in the
- // boot class path, access should be granted.
- appendToBootClassLoader(DEX_CHILD);
- doTest(true, true, false);
+ // Repeat the two tests above, only with parent being a core-platform dex file.
+ setDexDomain(parentIdx, /* isCorePlatform */ true);
+ doTest(DexDomain.CorePlatform, DexDomain.Application, false);
+ doUnloading();
+ doTest(DexDomain.CorePlatform, DexDomain.Application, true);
+ doUnloading();
+
+ // Append child to boot class path, first as a platform dex file.
+ // It should not be allowed to access non-public, non-core platform API members.
+ int childIdx = appendToBootClassLoader(DEX_CHILD, /* isCorePlatform */ false);
+ doTest(DexDomain.CorePlatform, DexDomain.Platform, false);
+ doUnloading();
+
+ // And finally change child to core-platform dex. With both in the boot classpath
+ // and both core-platform, access should be granted.
+ setDexDomain(childIdx, /* isCorePlatform */ true);
+ doTest(DexDomain.CorePlatform, DexDomain.CorePlatform, false);
doUnloading();
}
- private static void doTest(boolean parentInBoot, boolean childInBoot, boolean whitelistAllApis)
- throws Exception {
+ private static void doTest(DexDomain parentDomain, DexDomain childDomain,
+ boolean whitelistAllApis) throws Exception {
// Load parent dex if it is not in boot class path.
ClassLoader parentLoader = null;
- if (parentInBoot) {
- parentLoader = BOOT_CLASS_LOADER;
- } else {
+ if (parentDomain == DexDomain.Application) {
parentLoader = new PathClassLoader(DEX_PARENT, ClassLoader.getSystemClassLoader());
+ } else {
+ parentLoader = BOOT_CLASS_LOADER;
}
// Load child dex if it is not in boot class path.
ClassLoader childLoader = null;
- if (childInBoot) {
+ if (childDomain == DexDomain.Application) {
+ childLoader = new InMemoryDexClassLoader(readDexFile(DEX_CHILD), parentLoader);
+ } else {
if (parentLoader != BOOT_CLASS_LOADER) {
throw new IllegalStateException(
"DeclaringClass must be in parent class loader of CallingClass");
}
childLoader = BOOT_CLASS_LOADER;
- } else {
- childLoader = new InMemoryDexClassLoader(readDexFile(DEX_CHILD), parentLoader);
}
// Create a unique copy of the native library. Each shared library can only
// be loaded once, but for some reason even classes from a class loader
// cannot register their native methods against symbols in a shared library
// loaded by their parent class loader.
- String nativeLibCopy = createNativeLibCopy(parentInBoot, childInBoot, whitelistAllApis);
+ String nativeLibCopy = createNativeLibCopy(parentDomain, childDomain, whitelistAllApis);
if (whitelistAllApis) {
VMRuntime.getRuntime().setHiddenApiExemptions(new String[]{"L"});
}
// Invoke ChildClass.runTest
- Class.forName("ChildClass", true, childLoader)
- .getDeclaredMethod("runTest", String.class, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE)
- .invoke(null, nativeLibCopy, parentInBoot, childInBoot, whitelistAllApis);
+ Class<?> childClass = Class.forName("ChildClass", true, childLoader);
+ Method runTestMethod = childClass.getDeclaredMethod(
+ "runTest", String.class, Integer.TYPE, Integer.TYPE, Boolean.TYPE);
+ runTestMethod.invoke(null, nativeLibCopy, parentDomain.ordinal(), childDomain.ordinal(),
+ whitelistAllApis);
VMRuntime.getRuntime().setHiddenApiExemptions(new String[0]);
}
@@ -146,11 +171,11 @@
// Copy native library to a new file with a unique name so it does not
// conflict with other loaded instance of the same binary file.
- private static String createNativeLibCopy(
- boolean parentInBoot, boolean childInBoot, boolean whitelistAllApis) throws Exception {
+ private static String createNativeLibCopy(DexDomain parentDomain, DexDomain childDomain,
+ boolean whitelistAllApis) throws Exception {
String tempFileName = System.mapLibraryName(
- "hiddenapitest_" + (parentInBoot ? "1" : "0") + (childInBoot ? "1" : "0") +
- (whitelistAllApis ? "1" : "0"));
+ "hiddenapitest_" + (parentDomain.ordinal()) + (childDomain.ordinal()) +
+ (whitelistAllApis ? "1" : "0"));
File tempFile = new File(System.getenv("DEX_LOCATION"), tempFileName);
Files.copy(new File(nativeLibFileName).toPath(), tempFile.toPath());
return tempFile.getAbsolutePath();
@@ -175,6 +200,7 @@
private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader();
- private static native void appendToBootClassLoader(String dexPath);
+ private static native int appendToBootClassLoader(String dexPath, boolean isCorePlatform);
+ private static native void setDexDomain(int index, boolean isCorePlatform);
private static native void init();
}
diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java
index 3427b8e..f120bda 100644
--- a/test/674-hiddenapi/src-ex/ChildClass.java
+++ b/test/674-hiddenapi/src-ex/ChildClass.java
@@ -45,7 +45,8 @@
Whitelist(PrimitiveType.TShort),
LightGreylist(PrimitiveType.TBoolean),
DarkGreylist(PrimitiveType.TByte),
- Blacklist(PrimitiveType.TCharacter);
+ Blacklist(PrimitiveType.TCharacter),
+ BlacklistAndCorePlatformApi(PrimitiveType.TInteger);
Hiddenness(PrimitiveType type) { mAssociatedType = type; }
public PrimitiveType mAssociatedType;
@@ -67,19 +68,34 @@
Denied,
}
+ // This needs to be kept in sync with DexDomain in Main.
+ enum DexDomain {
+ CorePlatform,
+ Platform,
+ Application
+ }
+
private static final boolean booleanValues[] = new boolean[] { false, true };
- public static void runTest(String libFileName, boolean expectedParentInBoot,
- boolean expectedChildInBoot, boolean everythingWhitelisted) throws Exception {
+ public static void runTest(String libFileName, int parentDomainOrdinal,
+ int childDomainOrdinal, boolean everythingWhitelisted) throws Exception {
System.load(libFileName);
+ parentDomain = DexDomain.values()[parentDomainOrdinal];
+ childDomain = DexDomain.values()[childDomainOrdinal];
+
+ configMessage = "parentDomain=" + parentDomain.name() + ", childDomain=" + childDomain.name()
+ + ", everythingWhitelisted=" + everythingWhitelisted;
+
// Check expectations about loading into boot class path.
- isParentInBoot = (ParentClass.class.getClassLoader().getParent() == null);
+ boolean isParentInBoot = (ParentClass.class.getClassLoader().getParent() == null);
+ boolean expectedParentInBoot = (parentDomain != DexDomain.Application);
if (isParentInBoot != expectedParentInBoot) {
throw new RuntimeException("Expected ParentClass " +
(expectedParentInBoot ? "" : "not ") + "in boot class path");
}
- isChildInBoot = (ChildClass.class.getClassLoader().getParent() == null);
+ boolean isChildInBoot = (ChildClass.class.getClassLoader().getParent() == null);
+ boolean expectedChildInBoot = (childDomain != DexDomain.Application);
if (isChildInBoot != expectedChildInBoot) {
throw new RuntimeException("Expected ChildClass " + (expectedChildInBoot ? "" : "not ") +
"in boot class path");
@@ -92,14 +108,26 @@
// Run meaningful combinations of access flags.
for (Hiddenness hiddenness : Hiddenness.values()) {
final Behaviour expected;
+ final boolean invokesMemberCallback;
// Warnings are now disabled whenever access is granted, even for
// greylisted APIs. This is the behaviour for release builds.
- if (isSameBoot || everythingWhitelisted || hiddenness == Hiddenness.Whitelist) {
+ if (everythingWhitelisted || hiddenness == Hiddenness.Whitelist) {
expected = Behaviour.Granted;
- } else if (hiddenness == Hiddenness.Blacklist) {
+ invokesMemberCallback = false;
+ } else if (parentDomain == DexDomain.CorePlatform && childDomain == DexDomain.Platform) {
+ expected = (hiddenness == Hiddenness.BlacklistAndCorePlatformApi)
+ ? Behaviour.Granted : Behaviour.Denied;
+ invokesMemberCallback = false;
+ } else if (isSameBoot) {
+ expected = Behaviour.Granted;
+ invokesMemberCallback = false;
+ } else if (hiddenness == Hiddenness.Blacklist ||
+ hiddenness == Hiddenness.BlacklistAndCorePlatformApi) {
expected = Behaviour.Denied;
+ invokesMemberCallback = true;
} else {
expected = Behaviour.Warning;
+ invokesMemberCallback = true;
}
for (boolean isStatic : booleanValues) {
@@ -109,8 +137,10 @@
// Test reflection and JNI on methods and fields
for (Class klass : new Class<?>[] { ParentClass.class, ParentInterface.class }) {
String baseName = visibility.name() + suffix;
- checkField(klass, "field" + baseName, isStatic, visibility, expected);
- checkMethod(klass, "method" + baseName, isStatic, visibility, expected);
+ checkField(klass, "field" + baseName, isStatic, visibility, expected,
+ invokesMemberCallback);
+ checkMethod(klass, "method" + baseName, isStatic, visibility, expected,
+ invokesMemberCallback);
}
// Check whether one can use a class constructor.
@@ -118,7 +148,8 @@
// Check whether one can use an interface default method.
String name = "method" + visibility.name() + "Default" + hiddenness.name();
- checkMethod(ParentInterface.class, name, /*isStatic*/ false, visibility, expected);
+ checkMethod(ParentInterface.class, name, /*isStatic*/ false, visibility, expected,
+ invokesMemberCallback);
}
// Test whether static linking succeeds.
@@ -181,11 +212,10 @@
}
private static void checkField(Class<?> klass, String name, boolean isStatic,
- Visibility visibility, Behaviour behaviour) throws Exception {
+ Visibility visibility, Behaviour behaviour, boolean invokesMemberCallback) throws Exception {
boolean isPublic = (visibility == Visibility.Public);
boolean canDiscover = (behaviour != Behaviour.Denied);
- boolean invokesMemberCallback = (behaviour != Behaviour.Granted);
if (klass.isInterface() && (!isStatic || !isPublic)) {
// Interfaces only have public static fields.
@@ -275,7 +305,7 @@
}
private static void checkMethod(Class<?> klass, String name, boolean isStatic,
- Visibility visibility, Behaviour behaviour) throws Exception {
+ Visibility visibility, Behaviour behaviour, boolean invokesMemberCallback) throws Exception {
boolean isPublic = (visibility == Visibility.Public);
if (klass.isInterface() && !isPublic) {
@@ -284,7 +314,6 @@
}
boolean canDiscover = (behaviour != Behaviour.Denied);
- boolean invokesMemberCallback = (behaviour != Behaviour.Granted);
// Test discovery with reflection.
@@ -428,8 +457,7 @@
if (Reflection.canUseNewInstance(klass) != canAccess) {
throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
- "be able to construct " + klass.getName() + ". " +
- "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+ "be able to construct " + klass.getName() + ". " + configMessage);
}
}
@@ -439,8 +467,7 @@
if (Linking.canAccess(className, takesParameter) != canAccess) {
throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
- "be able to verify " + className + "." +
- "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+ "be able to verify " + className + "." + configMessage);
}
}
@@ -448,16 +475,13 @@
String fn, boolean canAccess) {
throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() +
"." + name + " to " + (canAccess ? "" : "not ") + "be discoverable with " + fn + ". " +
- "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " +
- "everythingWhitelisted = " + everythingWhitelisted);
+ configMessage);
}
private static void throwAccessException(Class<?> klass, String name, boolean isField,
String fn) {
throw new RuntimeException("Expected to be able to access " + (isField ? "field " : "method ") +
- klass.getName() + "." + name + " using " + fn + ". " +
- "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " +
- "everythingWhitelisted = " + everythingWhitelisted);
+ klass.getName() + "." + name + " using " + fn + ". " + configMessage);
}
private static void throwModifiersException(Class<?> klass, String name, boolean isField) {
@@ -465,7 +489,9 @@
"." + name + " to not expose hidden modifiers");
}
- private static boolean isParentInBoot;
- private static boolean isChildInBoot;
+ private static DexDomain parentDomain;
+ private static DexDomain childDomain;
private static boolean everythingWhitelisted;
+
+ private static String configMessage;
}
diff --git a/test/674-hiddenapi/src-ex/Linking.java b/test/674-hiddenapi/src-ex/Linking.java
index 0fa0b19..5aa3663 100644
--- a/test/674-hiddenapi/src-ex/Linking.java
+++ b/test/674-hiddenapi/src-ex/Linking.java
@@ -62,6 +62,12 @@
}
}
+class LinkFieldGetBlacklistAndCorePlatformApi {
+ public static int access() {
+ return new ParentClass().fieldPublicBlacklistAndCorePlatformApi;
+ }
+}
+
// INSTANCE FIELD SET
class LinkFieldSetWhitelist {
@@ -92,6 +98,13 @@
}
}
+class LinkFieldSetBlacklistAndCorePlatformApi {
+ public static void access(int x) {
+ // Need to use a different field from the getter to bypass DexCache.
+ new ParentClass().fieldPublicBlacklistAndCorePlatformApiB = x;
+ }
+}
+
// STATIC FIELD GET
class LinkFieldGetStaticWhitelist {
@@ -118,6 +131,12 @@
}
}
+class LinkFieldGetStaticBlacklistAndCorePlatformApi {
+ public static int access() {
+ return ParentClass.fieldPublicStaticBlacklistAndCorePlatformApi;
+ }
+}
+
// STATIC FIELD SET
class LinkFieldSetStaticWhitelist {
@@ -148,6 +167,13 @@
}
}
+class LinkFieldSetStaticBlacklistAndCorePlatformApi {
+ public static void access(int x) {
+ // Need to use a different field from the getter to bypass DexCache.
+ ParentClass.fieldPublicStaticBlacklistAndCorePlatformApiB = x;
+ }
+}
+
// INVOKE INSTANCE METHOD
class LinkMethodWhitelist {
@@ -174,6 +200,12 @@
}
}
+class LinkMethodBlacklistAndCorePlatformApi {
+ public static int access() {
+ return new ParentClass().methodPublicBlacklistAndCorePlatformApi();
+ }
+}
+
// INVOKE INSTANCE INTERFACE METHOD
class LinkMethodInterfaceWhitelist {
@@ -200,6 +232,12 @@
}
}
+class LinkMethodInterfaceBlacklistAndCorePlatformApi {
+ public static int access() {
+ return DummyClass.getInterfaceInstance().methodPublicBlacklistAndCorePlatformApi();
+ }
+}
+
// INVOKE STATIC METHOD
class LinkMethodStaticWhitelist {
@@ -226,6 +264,12 @@
}
}
+class LinkMethodStaticBlacklistAndCorePlatformApi {
+ public static int access() {
+ return ParentClass.methodPublicStaticBlacklistAndCorePlatformApi();
+ }
+}
+
// INVOKE INTERFACE STATIC METHOD
class LinkMethodInterfaceStaticWhitelist {
@@ -251,3 +295,9 @@
return ParentInterface.methodPublicStaticBlacklist();
}
}
+
+class LinkMethodInterfaceStaticBlacklistAndCorePlatformApi {
+ public static int access() {
+ return ParentInterface.methodPublicStaticBlacklistAndCorePlatformApi();
+ }
+}
diff --git a/test/674-hiddenapi/src/DummyClass.java b/test/674-hiddenapi/src/DummyClass.java
index 51281a2..afba747 100644
--- a/test/674-hiddenapi/src/DummyClass.java
+++ b/test/674-hiddenapi/src/DummyClass.java
@@ -19,6 +19,7 @@
public int methodPublicLightGreylist() { return 2; }
public int methodPublicDarkGreylist() { return 3; }
public int methodPublicBlacklist() { return 4; }
+ public int methodPublicBlacklistAndCorePlatformApi() { return 5; }
public static ParentInterface getInterfaceInstance() {
return new DummyClass();
diff --git a/test/674-hiddenapi/src/NullaryConstructorBlacklistAndCorePlatformApi.java b/test/674-hiddenapi/src/NullaryConstructorBlacklistAndCorePlatformApi.java
new file mode 100644
index 0000000..86af29e
--- /dev/null
+++ b/test/674-hiddenapi/src/NullaryConstructorBlacklistAndCorePlatformApi.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class NullaryConstructorBlacklistAndCorePlatformApi {
+ public NullaryConstructorBlacklistAndCorePlatformApi() { x = 22; }
+ public NullaryConstructorBlacklistAndCorePlatformApi(int y) { x = y; }
+ protected int x;
+}
diff --git a/test/674-hiddenapi/src/ParentClass.java b/test/674-hiddenapi/src/ParentClass.java
index 07e84cc..1442392 100644
--- a/test/674-hiddenapi/src/ParentClass.java
+++ b/test/674-hiddenapi/src/ParentClass.java
@@ -43,6 +43,12 @@
private int fieldPrivateBlacklist = 244;
public int fieldPublicBlacklistB = 245;
+ public int fieldPublicBlacklistAndCorePlatformApi = 251;
+ int fieldPackageBlacklistAndCorePlatformApi = 252;
+ protected int fieldProtectedBlacklistAndCorePlatformApi = 253;
+ private int fieldPrivateBlacklistAndCorePlatformApi = 254;
+ public int fieldPublicBlacklistAndCorePlatformApiB = 255;
+
// STATIC FIELD
public static int fieldPublicStaticWhitelist = 111;
@@ -69,6 +75,12 @@
private static int fieldPrivateStaticBlacklist = 144;
public static int fieldPublicStaticBlacklistB = 145;
+ public static int fieldPublicStaticBlacklistAndCorePlatformApi = 151;
+ static int fieldPackageStaticBlacklistAndCorePlatformApi = 152;
+ protected static int fieldProtectedStaticBlacklistAndCorePlatformApi = 153;
+ private static int fieldPrivateStaticBlacklistAndCorePlatformApi = 154;
+ public static int fieldPublicStaticBlacklistAndCorePlatformApiB = 155;
+
// INSTANCE METHOD
public int methodPublicWhitelist() { return 411; }
@@ -91,6 +103,11 @@
protected int methodProtectedBlacklist() { return 443; }
private int methodPrivateBlacklist() { return 444; }
+ public int methodPublicBlacklistAndCorePlatformApi() { return 451; }
+ int methodPackageBlacklistAndCorePlatformApi() { return 452; }
+ protected int methodProtectedBlacklistAndCorePlatformApi() { return 453; }
+ private int methodPrivateBlacklistAndCorePlatformApi() { return 454; }
+
// STATIC METHOD
public static int methodPublicStaticWhitelist() { return 311; }
@@ -113,6 +130,11 @@
protected static int methodProtectedStaticBlacklist() { return 343; }
private static int methodPrivateStaticBlacklist() { return 344; }
+ public static int methodPublicStaticBlacklistAndCorePlatformApi() { return 351; }
+ static int methodPackageStaticBlacklistAndCorePlatformApi() { return 352; }
+ protected static int methodProtectedStaticBlacklistAndCorePlatformApi() { return 353; }
+ private static int methodPrivateStaticBlacklistAndCorePlatformApi() { return 354; }
+
// CONSTRUCTOR
// Whitelist
@@ -139,6 +161,12 @@
protected ParentClass(long x, char y) {}
private ParentClass(double x, char y) {}
+ // Blacklist and CorePlatformApi
+ public ParentClass(int x, int y) {}
+ ParentClass(float x, int y) {}
+ protected ParentClass(long x, int y) {}
+ private ParentClass(double x, int y) {}
+
// HELPERS
public int callMethodPublicWhitelist() { return methodPublicWhitelist(); }
@@ -157,4 +185,15 @@
public int callMethodPackageBlacklist() { return methodPackageBlacklist(); }
public int callMethodProtectedBlacklist() { return methodProtectedBlacklist(); }
+ public int callMethodPublicBlacklistAndCorePlatformApi() {
+ return methodPublicBlacklistAndCorePlatformApi();
+ }
+
+ public int callMethodPackageBlacklistAndCorePlatformApi() {
+ return methodPackageBlacklistAndCorePlatformApi();
+ }
+
+ public int callMethodProtectedBlacklistAndCorePlatformApi() {
+ return methodProtectedBlacklistAndCorePlatformApi();
+ }
}
diff --git a/test/674-hiddenapi/src/ParentInterface.java b/test/674-hiddenapi/src/ParentInterface.java
index f79ac9d..1c5b58f 100644
--- a/test/674-hiddenapi/src/ParentInterface.java
+++ b/test/674-hiddenapi/src/ParentInterface.java
@@ -20,22 +20,26 @@
static int fieldPublicStaticLightGreylist = 12;
static int fieldPublicStaticDarkGreylist = 13;
static int fieldPublicStaticBlacklist = 14;
+ static int fieldPublicStaticBlacklistAndCorePlatformApi = 15;
// INSTANCE METHOD
int methodPublicWhitelist();
int methodPublicLightGreylist();
int methodPublicDarkGreylist();
int methodPublicBlacklist();
+ int methodPublicBlacklistAndCorePlatformApi();
// STATIC METHOD
static int methodPublicStaticWhitelist() { return 21; }
static int methodPublicStaticLightGreylist() { return 22; }
static int methodPublicStaticDarkGreylist() { return 23; }
static int methodPublicStaticBlacklist() { return 24; }
+ static int methodPublicStaticBlacklistAndCorePlatformApi() { return 25; }
// DEFAULT METHOD
default int methodPublicDefaultWhitelist() { return 31; }
default int methodPublicDefaultLightGreylist() { return 32; }
default int methodPublicDefaultDarkGreylist() { return 33; }
default int methodPublicDefaultBlacklist() { return 34; }
+ default int methodPublicDefaultBlacklistAndCorePlatformApi() { return 35; }
}
diff --git a/test/999-redefine-hiddenapi/src/Main.java b/test/999-redefine-hiddenapi/src/Main.java
index 3d9bbda..014ea16 100644
--- a/test/999-redefine-hiddenapi/src/Main.java
+++ b/test/999-redefine-hiddenapi/src/Main.java
@@ -27,7 +27,7 @@
init();
// Load the '-ex' APK and attach it to the boot class path.
- appendToBootClassLoader(DEX_EXTRA);
+ appendToBootClassLoader(DEX_EXTRA, /* isCorePlatform */ false);
// Find the test class in boot class loader and verify that its members are hidden.
Class<?> klass = Class.forName("art.Test999", true, BOOT_CLASS_LOADER);
@@ -67,7 +67,7 @@
private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader();
// Native functions. Note that these are implemented in 674-hiddenapi/hiddenapi.cc.
- private static native void appendToBootClassLoader(String dexPath);
+ private static native void appendToBootClassLoader(String dexPath, boolean isCorePlatform);
private static native void init();
/**