summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libartbase/base/sdk_version.h58
-rw-r--r--libdexfile/dex/hidden_api_access_flags.h21
-rw-r--r--libdexfile/dex/modifiers.h7
-rw-r--r--openjdkjvmti/ti_redefine.cc10
-rw-r--r--runtime/art_field.h4
-rw-r--r--runtime/art_method-inl.h154
-rw-r--r--runtime/art_method.cc60
-rw-r--r--runtime/art_method.h2
-rw-r--r--runtime/class_linker.cc19
-rw-r--r--runtime/dex/dex_file_annotations.cc4
-rw-r--r--runtime/entrypoints/entrypoint_utils-inl.h6
-rw-r--r--runtime/entrypoints/entrypoint_utils.cc5
-rw-r--r--runtime/hidden_api.cc92
-rw-r--r--runtime/hidden_api.h154
-rw-r--r--runtime/hidden_api_test.cc5
-rw-r--r--runtime/jni/java_vm_ext.cc3
-rw-r--r--runtime/mirror/class.h4
-rw-r--r--runtime/native/dalvik_system_VMRuntime.cc8
-rw-r--r--runtime/parsed_options.cc2
-rw-r--r--runtime/runtime.cc3
-rw-r--r--runtime/runtime.h8
-rw-r--r--runtime/runtime_options.cc1
-rw-r--r--runtime/runtime_options.def3
-rw-r--r--runtime/verifier/method_verifier.cc16
-rw-r--r--test/674-hiddenapi/hiddenapi.cc5
-rwxr-xr-xtest/999-redefine-hiddenapi/src-redefine/gen.sh12
-rw-r--r--test/999-redefine-hiddenapi/src/Main.java84
27 files changed, 457 insertions, 293 deletions
diff --git a/libartbase/base/sdk_version.h b/libartbase/base/sdk_version.h
new file mode 100644
index 0000000000..4372e5a02f
--- /dev/null
+++ b/libartbase/base/sdk_version.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_LIBARTBASE_BASE_SDK_VERSION_H_
+#define ART_LIBARTBASE_BASE_SDK_VERSION_H_
+
+#include <cstdint>
+#include <limits>
+
+namespace art {
+
+enum class SdkVersion : uint32_t {
+ kMin = 0u,
+ kUnset = 0u,
+ kL = 21u,
+ kL_MR1 = 22u,
+ kM = 23u,
+ kN = 24u,
+ kN_MR1 = 25u,
+ kO = 26u,
+ kO_MR1 = 27u,
+ kP = 28u,
+ kP_MR1 = 29u,
+ kMax = std::numeric_limits<uint32_t>::max(),
+};
+
+inline bool IsSdkVersionSetAndMoreThan(uint32_t lhs, SdkVersion rhs) {
+ return lhs != static_cast<uint32_t>(SdkVersion::kUnset) && lhs > static_cast<uint32_t>(rhs);
+}
+
+inline bool IsSdkVersionSetAndAtLeast(uint32_t lhs, SdkVersion rhs) {
+ return lhs != static_cast<uint32_t>(SdkVersion::kUnset) && lhs >= static_cast<uint32_t>(rhs);
+}
+
+inline bool IsSdkVersionSetAndAtMost(uint32_t lhs, SdkVersion rhs) {
+ return lhs != static_cast<uint32_t>(SdkVersion::kUnset) && lhs <= static_cast<uint32_t>(rhs);
+}
+
+inline bool IsSdkVersionSetAndLessThan(uint32_t lhs, SdkVersion rhs) {
+ return lhs != static_cast<uint32_t>(SdkVersion::kUnset) && lhs < static_cast<uint32_t>(rhs);
+}
+
+} // namespace art
+
+#endif // ART_LIBARTBASE_BASE_SDK_VERSION_H_
diff --git a/libdexfile/dex/hidden_api_access_flags.h b/libdexfile/dex/hidden_api_access_flags.h
index fd5c8654c3..77bfbc99b3 100644
--- a/libdexfile/dex/hidden_api_access_flags.h
+++ b/libdexfile/dex/hidden_api_access_flags.h
@@ -48,27 +48,6 @@ enum class ApiList {
kNoList,
};
-static const int kAccFlagsShift = CTZ(kAccHiddenApiBits);
-static_assert(IsPowerOfTwo((kAccHiddenApiBits >> kAccFlagsShift) + 1),
- "kAccHiddenApiBits are not continuous");
-
-inline ApiList DecodeFromRuntime(uint32_t runtime_access_flags) {
- // This is used in the fast path, only DCHECK here.
- DCHECK_EQ(runtime_access_flags & kAccIntrinsic, 0u);
- uint32_t int_value = (runtime_access_flags & kAccHiddenApiBits) >> kAccFlagsShift;
- return static_cast<ApiList>(int_value);
-}
-
-inline uint32_t EncodeForRuntime(uint32_t runtime_access_flags, ApiList value) {
- CHECK_EQ(runtime_access_flags & kAccIntrinsic, 0u);
-
- uint32_t hidden_api_flags = static_cast<uint32_t>(value) << kAccFlagsShift;
- CHECK_EQ(hidden_api_flags & ~kAccHiddenApiBits, 0u);
-
- runtime_access_flags &= ~kAccHiddenApiBits;
- return runtime_access_flags | hidden_api_flags;
-}
-
inline bool AreValidFlags(uint32_t flags) {
return flags <= static_cast<uint32_t>(ApiList::kBlacklist);
}
diff --git a/libdexfile/dex/modifiers.h b/libdexfile/dex/modifiers.h
index c4ea2d39b4..114c8e63e3 100644
--- a/libdexfile/dex/modifiers.h
+++ b/libdexfile/dex/modifiers.h
@@ -52,7 +52,7 @@ static constexpr uint32_t kAccObsoleteMethod = 0x00040000; // method (ru
static constexpr uint32_t kAccSkipAccessChecks = 0x00080000; // method (runtime, not native)
// Used by a class to denote that the verifier has attempted to check it at least once.
static constexpr uint32_t kAccVerificationAttempted = 0x00080000; // class (runtime)
-static constexpr uint32_t kAccSkipHiddenApiChecks = 0x00100000; // class (runtime)
+static constexpr uint32_t kAccSkipHiddenapiChecks = 0x00100000; // class (runtime)
// This is set by the class linker during LinkInterfaceMethods. It is used by a method to represent
// that it was copied from its declaring class into another class. All methods marked kAccMiranda
// and kAccDefaultConflict will have this bit set. Any kAccDefault method contained in the methods_
@@ -84,7 +84,8 @@ static constexpr uint32_t kAccMustCountLocks = 0x04000000; // method (ru
// virtual call.
static constexpr uint32_t kAccSingleImplementation = 0x08000000; // method (runtime)
-static constexpr uint32_t kAccHiddenApiBits = 0x30000000; // field, method
+static constexpr uint32_t kAccPublicApi = 0x10000000; // field, method
+static constexpr uint32_t kAccHiddenapiBits = 0x30000000; // field, method
// Non-intrinsics: Caches whether we can use fast-path in the interpreter invokes.
// Intrinsics: These bits are part of the intrinsic ordinal.
@@ -103,7 +104,7 @@ static constexpr uint32_t kAccClassIsFinalizable = 0x80000000;
// Continuous sequence of bits used to hold the ordinal of an intrinsic method. Flags
// which overlap are not valid when kAccIntrinsic is set.
-static constexpr uint32_t kAccIntrinsicBits = kAccHiddenApiBits |
+static constexpr uint32_t kAccIntrinsicBits = kAccHiddenapiBits |
kAccSingleImplementation | kAccMustCountLocks | kAccCompileDontBother | kAccDefaultConflict |
kAccPreviouslyWarm | kAccFastInterpreterToInterpreterInvoke;
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index 7cd10394d5..cabb758912 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -1427,6 +1427,11 @@ void Redefiner::ClassRedefinition::UpdateMethods(art::ObjPtr<art::mirror::Class>
method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(class_def, dex_method_idx));
// Clear all the intrinsics related flags.
method.SetNotIntrinsic();
+ // Disable hiddenapi checks when accessing this method.
+ // Redefining hiddenapi flags is unsupported for the same reasons as redefining
+ // access flags. Moreover, ArtMethod loses pointer to the old dex file, so just
+ // disable the checks completely for consistency.
+ method.SetAccessFlags(method.GetAccessFlags() | art::kAccPublicApi);
}
}
@@ -1445,6 +1450,11 @@ void Redefiner::ClassRedefinition::UpdateFields(art::ObjPtr<art::mirror::Class>
CHECK(new_field_id != nullptr);
// We only need to update the index since the other data in the ArtField cannot be updated.
field.SetDexFieldIndex(dex_file_->GetIndexForFieldId(*new_field_id));
+ // Disable hiddenapi checks when accessing this method.
+ // Redefining hiddenapi flags is unsupported for the same reasons as redefining
+ // access flags. Moreover, ArtField loses pointer to the old dex file, so just
+ // disable the checks completely for consistency.
+ field.SetAccessFlags(field.GetAccessFlags() | art::kAccPublicApi);
}
}
}
diff --git a/runtime/art_field.h b/runtime/art_field.h
index dc7f985b91..1cf7afa022 100644
--- a/runtime/art_field.h
+++ b/runtime/art_field.h
@@ -180,10 +180,6 @@ class ArtField final {
return (GetAccessFlags() & kAccVolatile) != 0;
}
- hiddenapi::ApiList GetHiddenApiAccessFlags() REQUIRES_SHARED(Locks::mutator_lock_) {
- return hiddenapi::DecodeFromRuntime(GetAccessFlags());
- }
-
// Returns an instance field with this offset in the given class or null if not found.
// If kExactOffset is true then we only find the matching offset, not the field containing the
// offset.
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index d8da9129ed..f2541160ff 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -367,160 +367,6 @@ inline bool ArtMethod::HasSingleImplementation() {
return (GetAccessFlags() & kAccSingleImplementation) != 0;
}
-inline hiddenapi::ApiList ArtMethod::GetHiddenApiAccessFlags()
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (UNLIKELY(IsIntrinsic())) {
- switch (static_cast<Intrinsics>(GetIntrinsic())) {
- case Intrinsics::kSystemArrayCopyChar:
- case Intrinsics::kStringGetCharsNoCheck:
- case Intrinsics::kReferenceGetReferent:
- case Intrinsics::kMemoryPeekByte:
- case Intrinsics::kMemoryPokeByte:
- case Intrinsics::kUnsafeCASInt:
- case Intrinsics::kUnsafeCASLong:
- case Intrinsics::kUnsafeCASObject:
- case Intrinsics::kUnsafeGet:
- case Intrinsics::kUnsafeGetAndAddInt:
- case Intrinsics::kUnsafeGetAndAddLong:
- case Intrinsics::kUnsafeGetAndSetInt:
- case Intrinsics::kUnsafeGetAndSetLong:
- case Intrinsics::kUnsafeGetAndSetObject:
- case Intrinsics::kUnsafeGetLong:
- case Intrinsics::kUnsafeGetLongVolatile:
- case Intrinsics::kUnsafeGetObject:
- case Intrinsics::kUnsafeGetObjectVolatile:
- case Intrinsics::kUnsafeGetVolatile:
- case Intrinsics::kUnsafePut:
- case Intrinsics::kUnsafePutLong:
- case Intrinsics::kUnsafePutLongOrdered:
- case Intrinsics::kUnsafePutLongVolatile:
- case Intrinsics::kUnsafePutObject:
- case Intrinsics::kUnsafePutObjectOrdered:
- case Intrinsics::kUnsafePutObjectVolatile:
- case Intrinsics::kUnsafePutOrdered:
- case Intrinsics::kUnsafePutVolatile:
- case Intrinsics::kUnsafeLoadFence:
- case Intrinsics::kUnsafeStoreFence:
- case Intrinsics::kUnsafeFullFence:
- case Intrinsics::kCRC32Update:
- // These intrinsics are on the light greylist and will fail a DCHECK in
- // SetIntrinsic() if their flags change on the respective dex methods.
- // Note that the DCHECK currently won't fail if the dex methods are
- // whitelisted, e.g. in the core image (b/77733081). As a result, we
- // might print warnings but we won't change the semantics.
- return hiddenapi::ApiList::kLightGreylist;
- case Intrinsics::kStringNewStringFromBytes:
- case Intrinsics::kStringNewStringFromChars:
- case Intrinsics::kStringNewStringFromString:
- case Intrinsics::kMemoryPeekIntNative:
- case Intrinsics::kMemoryPeekLongNative:
- case Intrinsics::kMemoryPeekShortNative:
- case Intrinsics::kMemoryPokeIntNative:
- case Intrinsics::kMemoryPokeLongNative:
- case Intrinsics::kMemoryPokeShortNative:
- return hiddenapi::ApiList::kDarkGreylist;
- case Intrinsics::kVarHandleFullFence:
- case Intrinsics::kVarHandleAcquireFence:
- case Intrinsics::kVarHandleReleaseFence:
- case Intrinsics::kVarHandleLoadLoadFence:
- case Intrinsics::kVarHandleStoreStoreFence:
- case Intrinsics::kVarHandleCompareAndExchange:
- case Intrinsics::kVarHandleCompareAndExchangeAcquire:
- case Intrinsics::kVarHandleCompareAndExchangeRelease:
- case Intrinsics::kVarHandleCompareAndSet:
- case Intrinsics::kVarHandleGet:
- case Intrinsics::kVarHandleGetAcquire:
- case Intrinsics::kVarHandleGetAndAdd:
- case Intrinsics::kVarHandleGetAndAddAcquire:
- case Intrinsics::kVarHandleGetAndAddRelease:
- case Intrinsics::kVarHandleGetAndBitwiseAnd:
- case Intrinsics::kVarHandleGetAndBitwiseAndAcquire:
- case Intrinsics::kVarHandleGetAndBitwiseAndRelease:
- case Intrinsics::kVarHandleGetAndBitwiseOr:
- case Intrinsics::kVarHandleGetAndBitwiseOrAcquire:
- case Intrinsics::kVarHandleGetAndBitwiseOrRelease:
- case Intrinsics::kVarHandleGetAndBitwiseXor:
- case Intrinsics::kVarHandleGetAndBitwiseXorAcquire:
- case Intrinsics::kVarHandleGetAndBitwiseXorRelease:
- case Intrinsics::kVarHandleGetAndSet:
- case Intrinsics::kVarHandleGetAndSetAcquire:
- case Intrinsics::kVarHandleGetAndSetRelease:
- case Intrinsics::kVarHandleGetOpaque:
- case Intrinsics::kVarHandleGetVolatile:
- case Intrinsics::kVarHandleSet:
- case Intrinsics::kVarHandleSetOpaque:
- case Intrinsics::kVarHandleSetRelease:
- case Intrinsics::kVarHandleSetVolatile:
- case Intrinsics::kVarHandleWeakCompareAndSet:
- case Intrinsics::kVarHandleWeakCompareAndSetAcquire:
- case Intrinsics::kVarHandleWeakCompareAndSetPlain:
- case Intrinsics::kVarHandleWeakCompareAndSetRelease:
- // These intrinsics are on the blacklist and will fail a DCHECK in
- // SetIntrinsic() if their flags change on the respective dex methods.
- // Note that the DCHECK currently won't fail if the dex methods are
- // whitelisted, e.g. in the core image (b/77733081). Given that they are
- // exclusively VarHandle intrinsics, they should not be used outside
- // tests that do not enable hidden API checks.
- return hiddenapi::ApiList::kBlacklist;
- default:
- // Remaining intrinsics are public API. We DCHECK that in SetIntrinsic().
- return hiddenapi::ApiList::kWhitelist;
- }
- } else {
- return hiddenapi::DecodeFromRuntime(GetAccessFlags());
- }
-}
-
-inline void ArtMethod::SetIntrinsic(uint32_t intrinsic) {
- // Currently we only do intrinsics for static/final methods or methods of final
- // classes. We don't set kHasSingleImplementation for those methods.
- DCHECK(IsStatic() || IsFinal() || GetDeclaringClass()->IsFinal()) <<
- "Potential conflict with kAccSingleImplementation";
- static const int kAccFlagsShift = CTZ(kAccIntrinsicBits);
- DCHECK_LE(intrinsic, kAccIntrinsicBits >> kAccFlagsShift);
- uint32_t intrinsic_bits = intrinsic << kAccFlagsShift;
- uint32_t new_value = (GetAccessFlags() & ~kAccIntrinsicBits) | kAccIntrinsic | intrinsic_bits;
- if (kIsDebugBuild) {
- uint32_t java_flags = (GetAccessFlags() & kAccJavaFlagsMask);
- bool is_constructor = IsConstructor();
- bool is_synchronized = IsSynchronized();
- bool skip_access_checks = SkipAccessChecks();
- bool is_fast_native = IsFastNative();
- bool is_critical_native = IsCriticalNative();
- bool is_copied = IsCopied();
- bool is_miranda = IsMiranda();
- bool is_default = IsDefault();
- bool is_default_conflict = IsDefaultConflicting();
- bool is_compilable = IsCompilable();
- bool must_count_locks = MustCountLocks();
- hiddenapi::ApiList hidden_api_flags = GetHiddenApiAccessFlags();
- SetAccessFlags(new_value);
- DCHECK_EQ(java_flags, (GetAccessFlags() & kAccJavaFlagsMask));
- DCHECK_EQ(is_constructor, IsConstructor());
- DCHECK_EQ(is_synchronized, IsSynchronized());
- DCHECK_EQ(skip_access_checks, SkipAccessChecks());
- DCHECK_EQ(is_fast_native, IsFastNative());
- DCHECK_EQ(is_critical_native, IsCriticalNative());
- DCHECK_EQ(is_copied, IsCopied());
- DCHECK_EQ(is_miranda, IsMiranda());
- DCHECK_EQ(is_default, IsDefault());
- DCHECK_EQ(is_default_conflict, IsDefaultConflicting());
- DCHECK_EQ(is_compilable, IsCompilable());
- DCHECK_EQ(must_count_locks, MustCountLocks());
- // Only DCHECK that we have preserved the hidden API access flags if the
- // original method was not on the whitelist. This is because the core image
- // does not have the access flags set (b/77733081). It is fine to hard-code
- // these because (a) warnings on greylist do not change semantics, and
- // (b) only VarHandle intrinsics are blacklisted at the moment and they
- // should not be used outside tests with disabled API checks.
- if (hidden_api_flags != hiddenapi::ApiList::kWhitelist) {
- DCHECK_EQ(hidden_api_flags, GetHiddenApiAccessFlags()) << PrettyMethod();
- }
- } else {
- SetAccessFlags(new_value);
- }
-}
-
template<ReadBarrierOption kReadBarrierOption, typename RootVisitorType>
void ArtMethod::VisitRoots(RootVisitorType& visitor, PointerSize pointer_size) {
if (LIKELY(!declaring_class_.IsNull())) {
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 9bf31edd52..abfdd5547d 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -33,6 +33,7 @@
#include "dex/dex_instruction.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "gc/accounting/card_table-inl.h"
+#include "hidden_api.h"
#include "interpreter/interpreter.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
@@ -682,23 +683,72 @@ bool ArtMethod::HasAnyCompiledCode() {
return GetOatMethodQuickCode(runtime->GetClassLinker()->GetImagePointerSize()) != nullptr;
}
+void ArtMethod::SetIntrinsic(uint32_t intrinsic) {
+ // Currently we only do intrinsics for static/final methods or methods of final
+ // classes. We don't set kHasSingleImplementation for those methods.
+ DCHECK(IsStatic() || IsFinal() || GetDeclaringClass()->IsFinal()) <<
+ "Potential conflict with kAccSingleImplementation";
+ static const int kAccFlagsShift = CTZ(kAccIntrinsicBits);
+ DCHECK_LE(intrinsic, kAccIntrinsicBits >> kAccFlagsShift);
+ uint32_t intrinsic_bits = intrinsic << kAccFlagsShift;
+ uint32_t new_value = (GetAccessFlags() & ~kAccIntrinsicBits) | kAccIntrinsic | intrinsic_bits;
+ if (kIsDebugBuild) {
+ uint32_t java_flags = (GetAccessFlags() & kAccJavaFlagsMask);
+ bool is_constructor = IsConstructor();
+ bool is_synchronized = IsSynchronized();
+ bool skip_access_checks = SkipAccessChecks();
+ bool is_fast_native = IsFastNative();
+ bool is_critical_native = IsCriticalNative();
+ bool is_copied = IsCopied();
+ bool is_miranda = IsMiranda();
+ bool is_default = IsDefault();
+ bool is_default_conflict = IsDefaultConflicting();
+ bool is_compilable = IsCompilable();
+ bool must_count_locks = MustCountLocks();
+ uint32_t hiddenapi_flags = hiddenapi::GetRuntimeFlags(this);
+ SetAccessFlags(new_value);
+ DCHECK_EQ(java_flags, (GetAccessFlags() & kAccJavaFlagsMask));
+ DCHECK_EQ(is_constructor, IsConstructor());
+ DCHECK_EQ(is_synchronized, IsSynchronized());
+ DCHECK_EQ(skip_access_checks, SkipAccessChecks());
+ DCHECK_EQ(is_fast_native, IsFastNative());
+ DCHECK_EQ(is_critical_native, IsCriticalNative());
+ DCHECK_EQ(is_copied, IsCopied());
+ DCHECK_EQ(is_miranda, IsMiranda());
+ DCHECK_EQ(is_default, IsDefault());
+ DCHECK_EQ(is_default_conflict, IsDefaultConflicting());
+ DCHECK_EQ(is_compilable, IsCompilable());
+ DCHECK_EQ(must_count_locks, MustCountLocks());
+ // Only DCHECK that we have preserved the hidden API access flags if the
+ // original method was not on the whitelist. This is because the core image
+ // does not have the access flags set (b/77733081). It is fine to hard-code
+ // these because (a) warnings on greylist do not change semantics, and
+ // (b) only VarHandle intrinsics are blacklisted at the moment and they
+ // should not be used outside tests with disabled API checks.
+ if ((hiddenapi_flags & kAccHiddenapiBits) == 0) {
+ DCHECK_EQ(hiddenapi_flags, hiddenapi::GetRuntimeFlags(this)) << PrettyMethod();
+ }
+ } else {
+ SetAccessFlags(new_value);
+ }
+}
+
void ArtMethod::SetNotIntrinsic() {
if (!IsIntrinsic()) {
return;
}
- // Query the hidden API access flags of the intrinsic.
- hiddenapi::ApiList intrinsic_api_list = GetHiddenApiAccessFlags();
+ // Read the existing hiddenapi flags.
+ uint32_t hiddenapi_runtime_flags = hiddenapi::GetRuntimeFlags(this);
// Clear intrinsic-related access flags.
ClearAccessFlags(kAccIntrinsic | kAccIntrinsicBits);
// Re-apply hidden API access flags now that the method is not an intrinsic.
- SetAccessFlags(hiddenapi::EncodeForRuntime(GetAccessFlags(), intrinsic_api_list));
- DCHECK_EQ(GetHiddenApiAccessFlags(), intrinsic_api_list);
+ SetAccessFlags(GetAccessFlags() | hiddenapi_runtime_flags);
+ DCHECK_EQ(hiddenapi_runtime_flags, hiddenapi::GetRuntimeFlags(this));
}
-
void ArtMethod::CopyFrom(ArtMethod* src, PointerSize image_pointer_size) {
memcpy(reinterpret_cast<void*>(this), reinterpret_cast<const void*>(src),
Size(image_pointer_size));
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 4e3ef5366a..5bbee92c14 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -343,8 +343,6 @@ class ArtMethod final {
AddAccessFlags(kAccMustCountLocks);
}
- hiddenapi::ApiList GetHiddenApiAccessFlags() REQUIRES_SHARED(Locks::mutator_lock_);
-
// Returns true if this method could be overridden by a default method.
bool IsOverridableByDefaultMethod() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 35379cc251..ce7dfaf8af 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3468,14 +3468,8 @@ void ClassLinker::LoadField(const ClassAccessor::Field& field,
dst->SetDexFieldIndex(field_idx);
dst->SetDeclaringClass(klass.Get());
- // Get access flags from the DexFile. If this is a boot class path class,
- // also set its runtime hidden API access flags.
- uint32_t access_flags = field.GetAccessFlags();
- if (klass->IsBootStrapClassLoaded()) {
- access_flags = hiddenapi::EncodeForRuntime(
- access_flags, static_cast<hiddenapi::ApiList>(field.GetHiddenapiFlags()));
- }
- dst->SetAccessFlags(access_flags);
+ // Get access flags from the DexFile and set hiddenapi runtime access flags.
+ dst->SetAccessFlags(field.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(field));
}
void ClassLinker::LoadMethod(const DexFile& dex_file,
@@ -3491,13 +3485,8 @@ void ClassLinker::LoadMethod(const DexFile& dex_file,
dst->SetDeclaringClass(klass.Get());
dst->SetCodeItemOffset(method.GetCodeItemOffset());
- // Get access flags from the DexFile. If this is a boot class path class,
- // also set its runtime hidden API access flags.
- uint32_t access_flags = method.GetAccessFlags();
- if (klass->IsBootStrapClassLoaded()) {
- access_flags = hiddenapi::EncodeForRuntime(
- access_flags, static_cast<hiddenapi::ApiList>(method.GetHiddenapiFlags()));
- }
+ // Get access flags from the DexFile and set hiddenapi runtime access flags.
+ uint32_t access_flags = method.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(method);
if (UNLIKELY(strcmp("finalize", method_name) == 0)) {
// Set finalizable flag on declaring class.
diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc
index 15398672b2..6434828298 100644
--- a/runtime/dex/dex_file_annotations.cc
+++ b/runtime/dex/dex_file_annotations.cc
@@ -22,6 +22,7 @@
#include "art_field-inl.h"
#include "art_method-inl.h"
+#include "base/sdk_version.h"
#include "class_linker-inl.h"
#include "class_root.h"
#include "dex/dex_file-inl.h"
@@ -129,8 +130,7 @@ ObjPtr<mirror::Object> CreateAnnotationMember(const ClassData& klass,
bool IsVisibilityCompatible(uint32_t actual, uint32_t expected) {
if (expected == DexFile::kDexVisibilityRuntime) {
- int32_t sdk_version = Runtime::Current()->GetTargetSdkVersion();
- if (sdk_version > 0 && sdk_version <= 23) {
+ if (IsSdkVersionSetAndAtMost(Runtime::Current()->GetTargetSdkVersion(), SdkVersion::kM)) {
return actual == DexFile::kDexVisibilityRuntime || actual == DexFile::kDexVisibilityBuild;
}
}
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 0b005e0851..2236e61d75 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -22,6 +22,7 @@
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/enums.h"
+#include "base/sdk_version.h"
#include "class_linker-inl.h"
#include "common_throws.h"
#include "dex/dex_file.h"
@@ -94,8 +95,9 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method,
// even going back from boot image methods to the same oat file. However, this is
// not currently implemented in the compiler. Therefore crossing dex file boundary
// indicates that the inlined definition is not the same as the one used at runtime.
- bool target_sdk_pre_p = Runtime::Current()->GetTargetSdkVersion() < 28;
- LOG(target_sdk_pre_p ? WARNING : FATAL)
+ bool target_sdk_at_least_p =
+ IsSdkVersionSetAndAtLeast(Runtime::Current()->GetTargetSdkVersion(), SdkVersion::kP);
+ LOG(target_sdk_at_least_p ? FATAL : WARNING)
<< "Inlined method resolution crossed dex file boundary: from "
<< method->PrettyMethod()
<< " in " << method->GetDexFile()->GetLocation() << "/"
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index 12136bf476..19498f386c 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -20,6 +20,7 @@
#include "art_method-inl.h"
#include "base/enums.h"
#include "base/mutex.h"
+#include "base/sdk_version.h"
#include "class_linker-inl.h"
#include "dex/dex_file-inl.h"
#include "entrypoints/entrypoint_utils-inl.h"
@@ -64,9 +65,9 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, cons
soa.Self()->AssertThreadSuspensionIsAllowable();
jobjectArray args_jobj = nullptr;
const JValue zero;
- int32_t target_sdk_version = Runtime::Current()->GetTargetSdkVersion();
+ uint32_t target_sdk_version = Runtime::Current()->GetTargetSdkVersion();
// Do not create empty arrays unless needed to maintain Dalvik bug compatibility.
- if (args.size() > 0 || (target_sdk_version > 0 && target_sdk_version <= 21)) {
+ if (args.size() > 0 || IsSdkVersionSetAndAtMost(target_sdk_version, SdkVersion::kL)) {
args_jobj = soa.Env()->NewObjectArray(args.size(), WellKnownClasses::java_lang_Object, nullptr);
if (args_jobj == nullptr) {
CHECK(soa.Self()->IsExceptionPending());
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index 3b7b938d50..188c5f353b 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -18,8 +18,13 @@
#include <nativehelper/scoped_local_ref.h>
+#include "art_field-inl.h"
+#include "art_method-inl.h"
#include "base/dumpable.h"
-#include "thread-current-inl.h"
+#include "base/sdk_version.h"
+#include "dex/class_accessor-inl.h"
+#include "scoped_thread_state_change.h"
+#include "thread-inl.h"
#include "well_known_classes.h"
#ifdef ART_TARGET_ANDROID
@@ -71,24 +76,19 @@ enum AccessContextFlags {
kAccessDenied = 1 << 1,
};
-static int32_t GetMaxAllowedSdkVersionForApiList(ApiList api_list) {
- SdkCodes sdk = SdkCodes::kVersionNone;
+static SdkVersion GetMaxAllowedSdkVersionForApiList(ApiList api_list) {
switch (api_list) {
case ApiList::kWhitelist:
case ApiList::kLightGreylist:
- sdk = SdkCodes::kVersionUnlimited;
- break;
+ return SdkVersion::kMax;
case ApiList::kDarkGreylist:
- sdk = SdkCodes::kVersionO_MR1;
- break;
+ return SdkVersion::kO_MR1;
case ApiList::kBlacklist:
- sdk = SdkCodes::kVersionNone;
- break;
+ return SdkVersion::kMin;
case ApiList::kNoList:
LOG(FATAL) << "Unexpected value";
UNREACHABLE();
}
- return static_cast<int32_t>(sdk);
}
MemberSignature::MemberSignature(ArtField* field) {
@@ -235,23 +235,82 @@ void MemberSignature::NotifyHiddenApiListener(AccessMethod access_method) {
}
}
-static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtField*) {
+static ALWAYS_INLINE bool CanUpdateRuntimeFlags(ArtField*) {
return true;
}
-static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtMethod* method) {
+static ALWAYS_INLINE bool CanUpdateRuntimeFlags(ArtMethod* method) {
return !method->IsIntrinsic();
}
template<typename T>
static ALWAYS_INLINE void MaybeWhitelistMember(Runtime* runtime, T* member)
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (CanUpdateMemberAccessFlags(member) && runtime->ShouldDedupeHiddenApiWarnings()) {
- member->SetAccessFlags(hiddenapi::EncodeForRuntime(
- member->GetAccessFlags(), hiddenapi::ApiList::kWhitelist));
+ if (CanUpdateRuntimeFlags(member) && runtime->ShouldDedupeHiddenApiWarnings()) {
+ member->SetAccessFlags(member->GetAccessFlags() | kAccPublicApi);
}
}
+static constexpr uint32_t kNoDexFlags = 0u;
+static constexpr uint32_t kInvalidDexFlags = static_cast<uint32_t>(-1);
+
+uint32_t GetDexFlags(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Class> declaring_class = field->GetDeclaringClass();
+ DCHECK(declaring_class != nullptr) << "Fields always have a declaring class";
+
+ const DexFile::ClassDef* class_def = declaring_class->GetClassDef();
+ if (class_def == nullptr) {
+ return kNoDexFlags;
+ }
+
+ uint32_t flags = kInvalidDexFlags;
+ DCHECK(!AreValidFlags(flags));
+
+ ClassAccessor accessor(declaring_class->GetDexFile(),
+ *class_def,
+ /* parse_hiddenapi_class_data= */ true);
+ auto fn_visit = [&](const ClassAccessor::Field& dex_field) {
+ if (dex_field.GetIndex() == field->GetDexFieldIndex()) {
+ flags = dex_field.GetHiddenapiFlags();
+ }
+ };
+ accessor.VisitFields(fn_visit, fn_visit);
+
+ CHECK_NE(flags, kInvalidDexFlags) << "Could not find flags for field " << field->PrettyField();
+ DCHECK(AreValidFlags(flags));
+ return flags;
+}
+
+uint32_t GetDexFlags(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Class> declaring_class = method->GetDeclaringClass();
+ if (declaring_class.IsNull()) {
+ DCHECK(method->IsRuntimeMethod());
+ return kNoDexFlags;
+ }
+
+ const DexFile::ClassDef* class_def = declaring_class->GetClassDef();
+ if (class_def == nullptr) {
+ return kNoDexFlags;
+ }
+
+ uint32_t flags = kInvalidDexFlags;
+ DCHECK(!AreValidFlags(flags));
+
+ ClassAccessor accessor(declaring_class->GetDexFile(),
+ *class_def,
+ /* parse_hiddenapi_class_data= */ true);
+ auto fn_visit = [&](const ClassAccessor::Method& dex_method) {
+ if (dex_method.GetIndex() == method->GetDexMethodIndex()) {
+ flags = dex_method.GetHiddenapiFlags();
+ }
+ };
+ accessor.VisitMethods(fn_visit, fn_visit);
+
+ CHECK_NE(flags, kInvalidDexFlags) << "Could not find flags for method " << method->PrettyMethod();
+ DCHECK(AreValidFlags(flags));
+ return flags;
+}
+
template<typename T>
bool ShouldDenyAccessToMemberImpl(T* member,
hiddenapi::ApiList api_list,
@@ -263,7 +322,8 @@ bool ShouldDenyAccessToMemberImpl(T* member,
const bool deny_access =
(policy == EnforcementPolicy::kEnabled) &&
- (runtime->GetTargetSdkVersion() > GetMaxAllowedSdkVersionForApiList(api_list));
+ IsSdkVersionSetAndMoreThan(runtime->GetTargetSdkVersion(),
+ GetMaxAllowedSdkVersionForApiList(api_list));
MemberSignature member_signature(member);
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index ed00e2a892..32bae1127b 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -17,10 +17,11 @@
#ifndef ART_RUNTIME_HIDDEN_API_H_
#define ART_RUNTIME_HIDDEN_API_H_
-#include "art_field-inl.h"
-#include "art_method-inl.h"
+#include "art_field.h"
+#include "art_method.h"
#include "base/mutex.h"
#include "dex/hidden_api_access_flags.h"
+#include "intrinsics_enum.h"
#include "mirror/class-inl.h"
#include "reflection.h"
#include "runtime.h"
@@ -116,13 +117,6 @@ class ScopedHiddenApiEnforcementPolicySetting {
// 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 {
@@ -164,12 +158,136 @@ class MemberSignature {
void NotifyHiddenApiListener(AccessMethod access_method);
};
+// Locates hiddenapi flags for `field` in the corresponding dex file.
+// NB: This is an O(N) operation, linear with the number of members in the class def.
+uint32_t GetDexFlags(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_);
+
+// Locates hiddenapi flags for `method` in the corresponding dex file.
+// NB: This is an O(N) operation, linear with the number of members in the class def.
+uint32_t GetDexFlags(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+
template<typename T>
bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_);
} // namespace detail
+// Returns access flags for the runtime representation of a class member (ArtField/ArtMember).
+ALWAYS_INLINE inline uint32_t CreateRuntimeFlags(const ClassAccessor::BaseItem& member) {
+ uint32_t runtime_flags = 0u;
+
+ uint32_t dex_flags = member.GetHiddenapiFlags();
+ DCHECK(AreValidFlags(dex_flags));
+
+ ApiList api_list = static_cast<hiddenapi::ApiList>(dex_flags);
+ if (api_list == ApiList::kWhitelist) {
+ runtime_flags |= kAccPublicApi;
+ }
+
+ DCHECK_EQ(runtime_flags & kAccHiddenapiBits, runtime_flags)
+ << "Runtime flags not in reserved access flags bits";
+ return runtime_flags;
+}
+
+// Extracts hiddenapi runtime flags from access flags of ArtField.
+ALWAYS_INLINE inline uint32_t GetRuntimeFlags(ArtField* field)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return field->GetAccessFlags() & kAccHiddenapiBits;
+}
+
+// Extracts hiddenapi runtime flags from access flags of ArtMethod.
+// Uses hardcoded values for intrinsics.
+ALWAYS_INLINE inline uint32_t GetRuntimeFlags(ArtMethod* method)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (UNLIKELY(method->IsIntrinsic())) {
+ switch (static_cast<Intrinsics>(method->GetIntrinsic())) {
+ case Intrinsics::kSystemArrayCopyChar:
+ case Intrinsics::kStringGetCharsNoCheck:
+ case Intrinsics::kReferenceGetReferent:
+ case Intrinsics::kMemoryPeekByte:
+ case Intrinsics::kMemoryPokeByte:
+ case Intrinsics::kUnsafeCASInt:
+ case Intrinsics::kUnsafeCASLong:
+ case Intrinsics::kUnsafeCASObject:
+ case Intrinsics::kUnsafeGet:
+ case Intrinsics::kUnsafeGetAndAddInt:
+ case Intrinsics::kUnsafeGetAndAddLong:
+ case Intrinsics::kUnsafeGetAndSetInt:
+ case Intrinsics::kUnsafeGetAndSetLong:
+ case Intrinsics::kUnsafeGetAndSetObject:
+ case Intrinsics::kUnsafeGetLong:
+ case Intrinsics::kUnsafeGetLongVolatile:
+ case Intrinsics::kUnsafeGetObject:
+ case Intrinsics::kUnsafeGetObjectVolatile:
+ case Intrinsics::kUnsafeGetVolatile:
+ case Intrinsics::kUnsafePut:
+ case Intrinsics::kUnsafePutLong:
+ case Intrinsics::kUnsafePutLongOrdered:
+ case Intrinsics::kUnsafePutLongVolatile:
+ case Intrinsics::kUnsafePutObject:
+ case Intrinsics::kUnsafePutObjectOrdered:
+ case Intrinsics::kUnsafePutObjectVolatile:
+ case Intrinsics::kUnsafePutOrdered:
+ case Intrinsics::kUnsafePutVolatile:
+ case Intrinsics::kUnsafeLoadFence:
+ case Intrinsics::kUnsafeStoreFence:
+ case Intrinsics::kUnsafeFullFence:
+ case Intrinsics::kCRC32Update:
+ case Intrinsics::kStringNewStringFromBytes:
+ case Intrinsics::kStringNewStringFromChars:
+ case Intrinsics::kStringNewStringFromString:
+ case Intrinsics::kMemoryPeekIntNative:
+ case Intrinsics::kMemoryPeekLongNative:
+ case Intrinsics::kMemoryPeekShortNative:
+ case Intrinsics::kMemoryPokeIntNative:
+ case Intrinsics::kMemoryPokeLongNative:
+ case Intrinsics::kMemoryPokeShortNative:
+ case Intrinsics::kVarHandleFullFence:
+ case Intrinsics::kVarHandleAcquireFence:
+ case Intrinsics::kVarHandleReleaseFence:
+ case Intrinsics::kVarHandleLoadLoadFence:
+ case Intrinsics::kVarHandleStoreStoreFence:
+ case Intrinsics::kVarHandleCompareAndExchange:
+ case Intrinsics::kVarHandleCompareAndExchangeAcquire:
+ case Intrinsics::kVarHandleCompareAndExchangeRelease:
+ case Intrinsics::kVarHandleCompareAndSet:
+ case Intrinsics::kVarHandleGet:
+ case Intrinsics::kVarHandleGetAcquire:
+ case Intrinsics::kVarHandleGetAndAdd:
+ case Intrinsics::kVarHandleGetAndAddAcquire:
+ case Intrinsics::kVarHandleGetAndAddRelease:
+ case Intrinsics::kVarHandleGetAndBitwiseAnd:
+ case Intrinsics::kVarHandleGetAndBitwiseAndAcquire:
+ case Intrinsics::kVarHandleGetAndBitwiseAndRelease:
+ case Intrinsics::kVarHandleGetAndBitwiseOr:
+ case Intrinsics::kVarHandleGetAndBitwiseOrAcquire:
+ case Intrinsics::kVarHandleGetAndBitwiseOrRelease:
+ case Intrinsics::kVarHandleGetAndBitwiseXor:
+ case Intrinsics::kVarHandleGetAndBitwiseXorAcquire:
+ case Intrinsics::kVarHandleGetAndBitwiseXorRelease:
+ case Intrinsics::kVarHandleGetAndSet:
+ case Intrinsics::kVarHandleGetAndSetAcquire:
+ case Intrinsics::kVarHandleGetAndSetRelease:
+ case Intrinsics::kVarHandleGetOpaque:
+ case Intrinsics::kVarHandleGetVolatile:
+ case Intrinsics::kVarHandleSet:
+ case Intrinsics::kVarHandleSetOpaque:
+ case Intrinsics::kVarHandleSetRelease:
+ case Intrinsics::kVarHandleSetVolatile:
+ case Intrinsics::kVarHandleWeakCompareAndSet:
+ case Intrinsics::kVarHandleWeakCompareAndSetAcquire:
+ case Intrinsics::kVarHandleWeakCompareAndSetPlain:
+ case Intrinsics::kVarHandleWeakCompareAndSetRelease:
+ return 0u;
+ default:
+ // Remaining intrinsics are public API. We DCHECK that in SetIntrinsic().
+ return kAccPublicApi;
+ }
+ } else {
+ return method->GetAccessFlags() & kAccHiddenapiBits;
+ }
+}
+
// 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
@@ -183,16 +301,9 @@ inline bool ShouldDenyAccessToMember(T* member,
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(member != nullptr);
- // Decode hidden API access flags.
- // NB Multiple threads might try to access (and overwrite) these simultaneously,
- // causing a race. We only do that if access has not been denied, so the race
- // cannot change Java semantics. We should, however, decode the access flags
- // once and use it throughout this function, otherwise we may get inconsistent
- // results, e.g. print whitelist warnings (b/78327881).
- ApiList api_list = member->GetHiddenApiAccessFlags();
-
- // Exit early if member is on the whitelist.
- if (api_list == ApiList::kWhitelist) {
+ // Exit early if member is public API. This flag is also set for non-boot class
+ // path fields/methods.
+ if ((GetRuntimeFlags(member) & kAccPublicApi) != 0) {
return false;
}
@@ -202,6 +313,11 @@ inline bool ShouldDenyAccessToMember(T* member,
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 = static_cast<hiddenapi::ApiList>(detail::GetDexFlags(member));
+
// Member is hidden and caller is not exempted. Enter slow path.
return detail::ShouldDenyAccessToMemberImpl(member, api_list, access_method);
}
diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc
index 627d9a7e1c..314d878c66 100644
--- a/runtime/hidden_api_test.cc
+++ b/runtime/hidden_api_test.cc
@@ -16,6 +16,7 @@
#include "hidden_api.h"
+#include "base/sdk_version.h"
#include "common_runtime_test.h"
#include "jni/jni_internal.h"
#include "proxy_test.h"
@@ -112,14 +113,14 @@ TEST_F(HiddenApiTest, CheckGetActionFromRuntimeFlags) {
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), false);
runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
- runtime_->SetTargetSdkVersion(static_cast<int32_t>(hiddenapi::detail::SdkCodes::kVersionO_MR1));
+ runtime_->SetTargetSdkVersion(static_cast<uint32_t>(SdkVersion::kO_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::kEnabled);
- runtime_->SetTargetSdkVersion(static_cast<int32_t>(hiddenapi::detail::SdkCodes::kVersionP));
+ runtime_->SetTargetSdkVersion(static_cast<uint32_t>(SdkVersion::kP));
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), true);
diff --git a/runtime/jni/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc
index 6769368ee4..a61a48a29b 100644
--- a/runtime/jni/java_vm_ext.cc
+++ b/runtime/jni/java_vm_ext.cc
@@ -23,6 +23,7 @@
#include "art_method-inl.h"
#include "base/dumpable.h"
#include "base/mutex-inl.h"
+#include "base/sdk_version.h"
#include "base/stl_util.h"
#include "base/systrace.h"
#include "check_jni.h"
@@ -1030,7 +1031,7 @@ bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
int version = (*jni_on_load)(this, nullptr);
- if (runtime_->GetTargetSdkVersion() != 0 && runtime_->GetTargetSdkVersion() <= 21) {
+ if (IsSdkVersionSetAndAtMost(runtime_->GetTargetSdkVersion(), SdkVersion::kL)) {
// Make sure that sigchain owns SIGSEGV.
EnsureFrontOfChain(SIGSEGV);
}
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index c38cc86c59..4e551adfde 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -211,12 +211,12 @@ class MANAGED Class final : public Object {
}
ALWAYS_INLINE bool ShouldSkipHiddenApiChecks() REQUIRES_SHARED(Locks::mutator_lock_) {
- return (GetAccessFlags() & kAccSkipHiddenApiChecks) != 0;
+ return (GetAccessFlags() & kAccSkipHiddenapiChecks) != 0;
}
ALWAYS_INLINE void SetSkipHiddenApiChecks() REQUIRES_SHARED(Locks::mutator_lock_) {
uint32_t flags = GetAccessFlags();
- SetAccessFlags(flags | kAccSkipHiddenApiChecks);
+ SetAccessFlags(flags | kAccSkipHiddenapiChecks);
}
ALWAYS_INLINE void SetRecursivelyInitialized() REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 49b71cd801..e213dc79b8 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -29,6 +29,7 @@ extern "C" void android_set_application_target_sdk_version(uint32_t version);
#include "arch/instruction_set.h"
#include "art_method-inl.h"
#include "base/enums.h"
+#include "base/sdk_version.h"
#include "class_linker-inl.h"
#include "common_throws.h"
#include "debugger.h"
@@ -256,12 +257,15 @@ static void VMRuntime_setTargetSdkVersionNative(JNIEnv*, jobject, jint target_sd
// where workarounds can be enabled.
// Note that targetSdkVersion may be CUR_DEVELOPMENT (10000).
// Note that targetSdkVersion may be 0, meaning "current".
- Runtime::Current()->SetTargetSdkVersion(target_sdk_version);
+ uint32_t uint_target_sdk_version =
+ target_sdk_version <= 0 ? static_cast<uint32_t>(SdkVersion::kUnset)
+ : static_cast<uint32_t>(target_sdk_version);
+ Runtime::Current()->SetTargetSdkVersion(uint_target_sdk_version);
#ifdef ART_TARGET_ANDROID
// This part is letting libc/dynamic linker know about current app's
// target sdk version to enable compatibility workarounds.
- android_set_application_target_sdk_version(static_cast<uint32_t>(target_sdk_version));
+ android_set_application_target_sdk_version(uint_target_sdk_version);
#endif
}
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 715792876d..33c85973b3 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -323,7 +323,7 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize
.WithValueMap({{"false", false}, {"true", true}})
.IntoKey(M::SlowDebug)
.Define("-Xtarget-sdk-version:_")
- .WithType<int>()
+ .WithType<unsigned int>()
.IntoKey(M::TargetSdkVersion)
.Define("-Xhidden-api-checks")
.IntoKey(M::HiddenApiChecks)
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 4720599a91..f016e874ca 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -61,6 +61,7 @@
#include "base/mutex.h"
#include "base/os.h"
#include "base/quasi_atomic.h"
+#include "base/sdk_version.h"
#include "base/stl_util.h"
#include "base/systrace.h"
#include "base/unix_file/fd_file.h"
@@ -253,7 +254,7 @@ Runtime::Runtime()
preinitialization_transactions_(),
verify_(verifier::VerifyMode::kNone),
allow_dex_file_fallback_(true),
- target_sdk_version_(kUnsetSdkVersion),
+ target_sdk_version_(static_cast<uint32_t>(SdkVersion::kUnset)),
implicit_null_checks_(false),
implicit_so_checks_(false),
implicit_suspend_checks_(false),
diff --git a/runtime/runtime.h b/runtime/runtime.h
index f5aa300649..3c057f3c41 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -583,11 +583,11 @@ class Runtime {
return is_running_on_memory_tool_;
}
- void SetTargetSdkVersion(int32_t version) {
+ void SetTargetSdkVersion(uint32_t version) {
target_sdk_version_ = version;
}
- int32_t GetTargetSdkVersion() const {
+ uint32_t GetTargetSdkVersion() const {
return target_sdk_version_;
}
@@ -793,8 +793,6 @@ class Runtime {
return jdwp_provider_;
}
- static constexpr int32_t kUnsetSdkVersion = 0u;
-
uint32_t GetVerifierLoggingThresholdMs() const {
return verifier_logging_threshold_ms_;
}
@@ -975,7 +973,7 @@ class Runtime {
std::vector<std::string> cpu_abilist_;
// Specifies target SDK version to allow workarounds for certain API levels.
- int32_t target_sdk_version_;
+ uint32_t target_sdk_version_;
// Implicit checks flags.
bool implicit_null_checks_; // NullPointer checks are implicit.
diff --git a/runtime/runtime_options.cc b/runtime/runtime_options.cc
index f8c680db44..12dab158e5 100644
--- a/runtime/runtime_options.cc
+++ b/runtime/runtime_options.cc
@@ -18,6 +18,7 @@
#include <memory>
+#include "base/sdk_version.h"
#include "base/utils.h"
#include "debugger.h"
#include "gc/heap.h"
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index ae1e08f10b..5cec309453 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -116,7 +116,8 @@ RUNTIME_OPTIONS_KEY (std::vector<std::string>, \
ImageCompilerOptions) // -Ximage-compiler-option ...
RUNTIME_OPTIONS_KEY (verifier::VerifyMode, \
Verify, verifier::VerifyMode::kEnable)
-RUNTIME_OPTIONS_KEY (int, TargetSdkVersion, Runtime::kUnsetSdkVersion)
+RUNTIME_OPTIONS_KEY (unsigned int, TargetSdkVersion, \
+ static_cast<unsigned int>(SdkVersion::kUnset))
RUNTIME_OPTIONS_KEY (Unit, HiddenApiChecks)
RUNTIME_OPTIONS_KEY (std::string, NativeBridge)
RUNTIME_OPTIONS_KEY (unsigned int, ZygoteMaxFailedBoots, 10)
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 7b07389057..0b33a0b3c0 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -28,6 +28,7 @@
#include "base/indenter.h"
#include "base/logging.h" // For VLOG.
#include "base/mutex-inl.h"
+#include "base/sdk_version.h"
#include "base/stl_util.h"
#include "base/systrace.h"
#include "base/time_utils.h"
@@ -3677,9 +3678,10 @@ const RegType& MethodVerifier::ResolveClass(dex::TypeIndex class_idx) {
// Note: we do this for unresolved classes to trigger re-verification at runtime.
if (C == CheckAccess::kYes &&
result->IsNonZeroReferenceTypes() &&
- (api_level_ >= 28u || !result->IsUnresolvedTypes())) {
+ (IsSdkVersionSetAndAtLeast(api_level_, SdkVersion::kP) || !result->IsUnresolvedTypes())) {
const RegType& referrer = GetDeclaringClass();
- if ((api_level_ >= 28u || !referrer.IsUnresolvedTypes()) && !referrer.CanAccess(*result)) {
+ if ((IsSdkVersionSetAndAtLeast(api_level_, SdkVersion::kP) || !referrer.IsUnresolvedTypes()) &&
+ !referrer.CanAccess(*result)) {
Fail(VERIFY_ERROR_ACCESS_CLASS) << "(possibly) illegal class access: '"
<< referrer << "' -> '" << *result << "'";
}
@@ -4562,7 +4564,9 @@ ArtField* MethodVerifier::GetStaticField(int field_idx) {
}
if (klass_type.IsUnresolvedTypes()) {
// Accessibility checks depend on resolved fields.
- DCHECK(klass_type.Equals(GetDeclaringClass()) || !failures_.empty() || api_level_ < 28u);
+ DCHECK(klass_type.Equals(GetDeclaringClass()) ||
+ !failures_.empty() ||
+ IsSdkVersionSetAndLessThan(api_level_, SdkVersion::kP));
return nullptr; // Can't resolve Class so no more to do here, will do checking at runtime.
}
@@ -4603,7 +4607,9 @@ ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_id
}
if (klass_type.IsUnresolvedTypes()) {
// Accessibility checks depend on resolved fields.
- DCHECK(klass_type.Equals(GetDeclaringClass()) || !failures_.empty() || api_level_ < 28u);
+ DCHECK(klass_type.Equals(GetDeclaringClass()) ||
+ !failures_.empty() ||
+ IsSdkVersionSetAndLessThan(api_level_, SdkVersion::kP));
return nullptr; // Can't resolve Class so no more to do here
}
@@ -4739,7 +4745,7 @@ void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, const RegType&
DCHECK(!can_load_classes_ || self_->IsExceptionPending());
self_->ClearException();
}
- } else if (api_level_ >= 28u) {
+ } else if (IsSdkVersionSetAndAtLeast(api_level_, SdkVersion::kP)) {
// If we don't have the field (it seems we failed resolution) and this is a PUT, we need to
// redo verification at runtime as the field may be final, unless the field id shows it's in
// the same class.
diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc
index 8e3e4eb5ce..d11aa579e5 100644
--- a/test/674-hiddenapi/hiddenapi.cc
+++ b/test/674-hiddenapi/hiddenapi.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "base/sdk_version.h"
#include "class_linker.h"
#include "dex/art_dex_file_loader.h"
#include "hidden_api.h"
@@ -29,7 +30,7 @@ namespace Test674HiddenApi {
extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) {
Runtime* runtime = Runtime::Current();
runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
- runtime->SetTargetSdkVersion(static_cast<int32_t>(hiddenapi::detail::SdkCodes::kVersionO_MR1));
+ runtime->SetTargetSdkVersion(static_cast<uint32_t>(SdkVersion::kO_MR1));
runtime->SetDedupeHiddenApiWarnings(false);
}
@@ -284,7 +285,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeConstructorV(
}
extern "C" JNIEXPORT jint JNICALL Java_Reflection_getHiddenApiAccessFlags(JNIEnv*, jclass) {
- return static_cast<jint>(kAccHiddenApiBits);
+ return static_cast<jint>(kAccHiddenapiBits);
}
} // namespace Test674HiddenApi
diff --git a/test/999-redefine-hiddenapi/src-redefine/gen.sh b/test/999-redefine-hiddenapi/src-redefine/gen.sh
index 6948cbbfc3..f78a025265 100755
--- a/test/999-redefine-hiddenapi/src-redefine/gen.sh
+++ b/test/999-redefine-hiddenapi/src-redefine/gen.sh
@@ -18,13 +18,21 @@ set -e
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
TMP=`mktemp -d`
-CLASS "art/Test999"
+CLASS="art/Test999"
-(cd "$TMP" && javac -d "${TMP}" "$DIR/${CLASS}.java" && d8 --output . "$TMP/${CLASS}.class")
+(cd "$TMP" && \
+ javac -d "${TMP}" "$DIR/${CLASS}.java" && \
+ d8 --output . "$TMP/${CLASS}.class" &&
+ hiddenapi encode --input-dex="$TMP/classes.dex" \
+ --output-dex="$TMP/classes-hiddenapi.dex" \
+ --flags="$DIR/../hiddenapi-flags.csv" \
+ --no-force-assign-all)
echo ' private static final byte[] CLASS_BYTES = Base64.getDecoder().decode('
base64 "${TMP}/${CLASS}.class" | sed -E 's/^/ "/' | sed ':a;N;$!ba;s/\n/" +\n/g' | sed -E '$ s/$/");/'
echo ' private static final byte[] DEX_BYTES = Base64.getDecoder().decode('
base64 "${TMP}/classes.dex" | sed -E 's/^/ "/' | sed ':a;N;$!ba;s/\n/" +\n/g' | sed -E '$ s/$/");/'
+echo ' private static final byte[] DEX_BYTES_HIDDEN = Base64.getDecoder().decode('
+base64 "${TMP}/classes-hiddenapi.dex" | sed -E 's/^/ "/' | sed ':a;N;$!ba;s/\n/" +\n/g' | sed -E '$ s/$/");/'
rm -rf "$TMP"
diff --git a/test/999-redefine-hiddenapi/src/Main.java b/test/999-redefine-hiddenapi/src/Main.java
index c6365ac234..4627b4fd22 100644
--- a/test/999-redefine-hiddenapi/src/Main.java
+++ b/test/999-redefine-hiddenapi/src/Main.java
@@ -19,7 +19,7 @@ import java.lang.reflect.Method;
import java.util.Base64;
public class Main {
- public static void main(String[] args) throws Exception {
+ public static void main(String[] args) throws ClassNotFoundException {
System.loadLibrary(args[0]);
// Run the initialization routine. This will enable hidden API checks in
@@ -31,35 +31,53 @@ public class Main {
// 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);
- assertMethodIsHidden(klass, "before redefinition");
- assertFieldIsHidden(klass, "before redefinition");
+ assertMethodIsHidden(true, klass, "before redefinition");
+ assertFieldIsHidden(true, klass, "before redefinition");
- // Redefine the class using JVMTI.
+ // Redefine the class using JVMTI. Use dex file without hiddenapi flags.
art.Redefinition.setTestConfiguration(art.Redefinition.Config.COMMON_REDEFINE);
art.Redefinition.doCommonClassRedefinition(klass, CLASS_BYTES, DEX_BYTES);
- // Verify that the class members are still hidden.
- assertMethodIsHidden(klass, "after redefinition");
- assertFieldIsHidden(klass, "after redefinition");
+ // Verify that the class members are not hidden anymore.
+ assertMethodIsHidden(false, klass, "after first redefinition");
+ assertFieldIsHidden(false, klass, "after first redefinition");
+
+ // Redefine the class using JVMTI, this time with a dex file with hiddenapi flags.
+ art.Redefinition.setTestConfiguration(art.Redefinition.Config.COMMON_REDEFINE);
+ art.Redefinition.doCommonClassRedefinition(klass, CLASS_BYTES, DEX_BYTES_HIDDEN);
+
+ // Verify that the class members are still accessible.
+ assertMethodIsHidden(false, klass, "after second redefinition");
+ assertFieldIsHidden(false, klass, "after second redefinition");
}
- private static void assertMethodIsHidden(Class<?> klass, String msg) throws Exception {
+ private static void assertMethodIsHidden(boolean expectedHidden, Class<?> klass, String msg) {
try {
klass.getDeclaredMethod("foo");
- // Unexpected. Should have thrown NoSuchMethodException.
- throw new Exception("Method should not be accessible " + msg);
+ if (expectedHidden) {
+ // Unexpected. Should have thrown NoSuchMethodException.
+ throw new RuntimeException("Method should not be accessible " + msg);
+ }
} catch (NoSuchMethodException ex) {
- // Expected.
+ if (!expectedHidden) {
+ // Unexpected. Should not have thrown NoSuchMethodException.
+ throw new RuntimeException("Method should be accessible " + msg);
+ }
}
}
- private static void assertFieldIsHidden(Class<?> klass, String msg) throws Exception {
+ private static void assertFieldIsHidden(boolean expectedHidden, Class<?> klass, String msg) {
try {
klass.getDeclaredField("bar");
- // Unexpected. Should have thrown NoSuchFieldException.
- throw new Exception("Field should not be accessible " + msg);
+ if (expectedHidden) {
+ // Unexpected. Should have thrown NoSuchFieldException.
+ throw new RuntimeException("Field should not be accessible " + msg);
+ }
} catch (NoSuchFieldException ex) {
- // Expected.
+ if (!expectedHidden) {
+ // Unexpected. Should not have thrown NoSuchFieldException.
+ throw new RuntimeException("Field should be accessible " + msg);
+ }
}
}
@@ -93,19 +111,37 @@ public class Main {
"ASoQQLUAArEAAAABAA0AAAAKAAIAAAATAAQAGAABAA4ACwABAAwAAAAlAAIAAQAAAAmyAAMSBLYA" +
"BbEAAAABAA0AAAAKAAIAAAAVAAgAFgABAA8AAAACABA=");
private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
- "ZGV4CjAzNQD0dZ+IWxOi+cJDSWjfTnUerlZj1Lll3ONIAwAAcAAAAHhWNBIAAAAAAAAAAJwCAAAQ" +
- "AAAAcAAAAAcAAACwAAAAAgAAAMwAAAACAAAA5AAAAAQAAAD0AAAAAQAAABQBAAAUAgAANAEAAIYB" +
+ "ZGV4CjAzNQDlfmgFfKulToQpDF+P4dsgeOkgfzzH+5lgAwAAcAAAAHhWNBIAAAAAAAAAALQCAAAQ" +
+ "AAAAcAAAAAcAAACwAAAAAgAAAMwAAAACAAAA5AAAAAQAAAD0AAAAAQAAABQBAAAsAgAANAEAAIYB" +
+ "AACOAQAAlwEAAJoBAACpAQAAwAEAANQBAADoAQAA/AEAAAoCAAANAgAAEQIAABYCAAAbAgAAIAIA" +
+ "ACkCAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAACAAQAA" +
+ "AQAAAAsAAAAFAAIADQAAAAEAAAAAAAAAAQAAAAwAAAACAAEADgAAAAMAAAAAAAAAAQAAAAEAAAAD" +
+ "AAAAAAAAAAgAAAAAAAAAoAIAAAAAAAACAAEAAQAAAHQBAAAIAAAAcBADAAEAEwBAAFkQAAAOAAMA" +
+ "AQACAAAAeQEAAAgAAABiAAEAGgEBAG4gAgAQAA4AEwAOQAAVAA54AAAAAQAAAAQABjxpbml0PgAH" +
+ "R29vZGJ5ZQABSQANTGFydC9UZXN0OTk5OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9s" +
+ "YW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0" +
+ "OTk5LmphdmEAAVYAAlZMAANiYXIAA2ZvbwADb3V0AAdwcmludGxuAHV+fkQ4eyJjb21waWxhdGlv" +
+ "bi1tb2RlIjoiZGVidWciLCJtaW4tYXBpIjoxLCJzaGEtMSI6ImQyMmFiNGYxOWI3NTYxNDQ3NTI4" +
+ "NTdjYTg2YjJjZWU0ZGQ5Y2ExNjYiLCJ2ZXJzaW9uIjoiMS40LjktZGV2In0AAAEBAQABAIGABLQC" +
+ "AQHUAgAAAAAOAAAAAAAAAAEAAAAAAAAAAQAAABAAAABwAAAAAgAAAAcAAACwAAAAAwAAAAIAAADM" +
+ "AAAABAAAAAIAAADkAAAABQAAAAQAAAD0AAAABgAAAAEAAAAUAQAAASAAAAIAAAA0AQAAAyAAAAIA" +
+ "AAB0AQAAARAAAAEAAACAAQAAAiAAABAAAACGAQAAACAAAAEAAACgAgAAAxAAAAEAAACwAgAAABAA" +
+ "AAEAAAC0AgAA");
+ private static final byte[] DEX_BYTES_HIDDEN = Base64.getDecoder().decode(
+ "ZGV4CjAzNQDsgG5ufKulToQpDF+P4dsgeOkgfzzH+5l4AwAAcAAAAHhWNBIAAAAAAAAAAMACAAAQ" +
+ "AAAAcAAAAAcAAACwAAAAAgAAAMwAAAACAAAA5AAAAAQAAAD0AAAAAQAAABQBAABEAgAANAEAAIYB" +
"AACOAQAAlwEAAJoBAACpAQAAwAEAANQBAADoAQAA/AEAAAoCAAANAgAAEQIAABYCAAAbAgAAIAIA" +
"ACkCAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAACAAQAA" +
"AQAAAAsAAAAFAAIADQAAAAEAAAAAAAAAAQAAAAwAAAACAAEADgAAAAMAAAAAAAAAAQAAAAEAAAAD" +
- "AAAAAAAAAAgAAAAAAAAAhwIAAAAAAAACAAEAAQAAAHQBAAAIAAAAcBADAAEAEwBAAFkQAAAOAAMA" +
+ "AAAAAAAAAAgAAAAAAAAAoAIAAAAAAAACAAEAAQAAAHQBAAAIAAAAcBADAAEAEwBAAFkQAAAOAAMA" +
"AQACAAAAeQEAAAgAAABiAAEAGgEBAG4gAgAQAA4AEwAOQAAVAA54AAAAAQAAAAQABjxpbml0PgAH" +
"R29vZGJ5ZQABSQANTGFydC9UZXN0OTk5OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9s" +
"YW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0" +
- "OTk5LmphdmEAAVYAAlZMAANiYXIAA2ZvbwADb3V0AAdwcmludGxuAFx+fkQ4eyJtaW4tYXBpIjox" +
- "LCJzaGEtMSI6IjU2YzJlMzBmNTIzM2I4NDRmZjZkZGQ4N2ZiNTNkMzRmYjE3MjM3ZGYiLCJ2ZXJz" +
- "aW9uIjoidjEuMi4xNS1kZXYifQAAAQEBAAEAgYAEtAIBAdQCAAAAAAAOAAAAAAAAAAEAAAAAAAAA" +
- "AQAAABAAAABwAAAAAgAAAAcAAACwAAAAAwAAAAIAAADMAAAABAAAAAIAAADkAAAABQAAAAQAAAD0" +
- "AAAABgAAAAEAAAAUAQAAASAAAAIAAAA0AQAAAyAAAAIAAAB0AQAAARAAAAEAAACAAQAAAiAAABAA" +
- "AACGAQAAACAAAAEAAACHAgAAAxAAAAEAAACYAgAAABAAAAEAAACcAgAA");
+ "OTk5LmphdmEAAVYAAlZMAANiYXIAA2ZvbwADb3V0AAdwcmludGxuAHV+fkQ4eyJjb21waWxhdGlv" +
+ "bi1tb2RlIjoiZGVidWciLCJtaW4tYXBpIjoxLCJzaGEtMSI6ImQyMmFiNGYxOWI3NTYxNDQ3NTI4" +
+ "NTdjYTg2YjJjZWU0ZGQ5Y2ExNjYiLCJ2ZXJzaW9uIjoiMS40LjktZGV2In0AAAEBAQABAIGABLQC" +
+ "AQHUAgAAAAALAAAACAAAAAIAAgAPAAAAAAAAAAEAAAAAAAAAAQAAABAAAABwAAAAAgAAAAcAAACw" +
+ "AAAAAwAAAAIAAADMAAAABAAAAAIAAADkAAAABQAAAAQAAAD0AAAABgAAAAEAAAAUAQAAASAAAAIA" +
+ "AAA0AQAAAyAAAAIAAAB0AQAAARAAAAEAAACAAQAAAiAAABAAAACGAQAAACAAAAEAAACgAgAAAxAA" +
+ "AAEAAACwAgAAAPAAAAEAAAC0AgAAABAAAAEAAADAAgAA");
}