summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--openjdkjvmti/ti_redefine.cc26
-rw-r--r--runtime/class_linker_test.cc4
-rw-r--r--runtime/hidden_api.cc143
-rw-r--r--runtime/hidden_api.h12
-rw-r--r--runtime/mirror/class_ext.cc12
-rw-r--r--runtime/mirror/class_ext.h18
-rwxr-xr-xtest/999-redefine-hiddenapi/src-redefine/gen.sh6
-rw-r--r--test/999-redefine-hiddenapi/src/Main.java59
8 files changed, 166 insertions, 114 deletions
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index cabb758912..6ca4e3846c 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -1427,11 +1427,6 @@ 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);
}
}
@@ -1450,11 +1445,6 @@ 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);
}
}
}
@@ -1469,15 +1459,25 @@ void Redefiner::ClassRedefinition::UpdateClass(
UpdateMethods(mclass, class_def);
UpdateFields(mclass);
+ art::ObjPtr<art::mirror::ClassExt> ext(mclass->GetExtData());
+ CHECK(!ext.IsNull());
+ ext->SetOriginalDexFile(original_dex_file);
+
+ // If this is the first time the class is being redefined, store
+ // the native DexFile pointer and initial ClassDef index in ClassExt.
+ // This preserves the pointer for hiddenapi access checks which need
+ // to read access flags from the initial DexFile.
+ if (ext->GetPreRedefineDexFile() == nullptr) {
+ ext->SetPreRedefineDexFile(&mclass->GetDexFile());
+ ext->SetPreRedefineClassDefIndex(mclass->GetDexClassDefIndex());
+ }
+
// Update the class fields.
// Need to update class last since the ArtMethod gets its DexFile from the class (which is needed
// to call GetReturnTypeDescriptor and GetParameterTypeList above).
mclass->SetDexCache(new_dex_cache.Ptr());
mclass->SetDexClassDefIndex(dex_file_->GetIndexForClassDef(class_def));
mclass->SetDexTypeIndex(dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(class_sig_.c_str())));
- art::ObjPtr<art::mirror::ClassExt> ext(mclass->GetExtData());
- CHECK(!ext.IsNull());
- ext->SetOriginalDexFile(original_dex_file);
// Notify the jit that all the methods in this class were redefined. Need to do this last since
// the jit relies on the dex_file_ being correct (for native methods at least) to find the method
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 56fdd06ff2..fe45b9e1f0 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -611,6 +611,10 @@ struct ClassExtOffsets : public CheckOffsets<mirror::ClassExt> {
addOffset(OFFSETOF_MEMBER(mirror::ClassExt, obsolete_dex_caches_), "obsoleteDexCaches");
addOffset(OFFSETOF_MEMBER(mirror::ClassExt, obsolete_methods_), "obsoleteMethods");
addOffset(OFFSETOF_MEMBER(mirror::ClassExt, original_dex_file_), "originalDexFile");
+ addOffset(OFFSETOF_MEMBER(mirror::ClassExt, pre_redefine_class_def_index_),
+ "preRedefineClassDefIndex");
+ addOffset(OFFSETOF_MEMBER(mirror::ClassExt, pre_redefine_dex_file_ptr_),
+ "preRedefineDexFilePtr");
addOffset(OFFSETOF_MEMBER(mirror::ClassExt, verify_error_), "verifyError");
}
};
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index 6cdba73c30..e0939ddbdb 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -21,7 +21,10 @@
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/dumpable.h"
+#include "class_root.h"
#include "dex/class_accessor-inl.h"
+#include "dex/dex_file_loader.h"
+#include "mirror/class_ext.h"
#include "scoped_thread_state_change.h"
#include "thread-inl.h"
#include "well_known_classes.h"
@@ -93,6 +96,24 @@ MemberSignature::MemberSignature(ArtMethod* method) {
type_ = kMethod;
}
+MemberSignature::MemberSignature(const ClassAccessor::Field& field) {
+ const DexFile& dex_file = field.GetDexFile();
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(field.GetIndex());
+ class_name_ = dex_file.GetFieldDeclaringClassDescriptor(field_id);
+ member_name_ = dex_file.GetFieldName(field_id);
+ type_signature_ = dex_file.GetFieldTypeDescriptor(field_id);
+ type_ = kField;
+}
+
+MemberSignature::MemberSignature(const ClassAccessor::Method& method) {
+ const DexFile& dex_file = method.GetDexFile();
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(method.GetIndex());
+ class_name_ = dex_file.GetMethodDeclaringClassDescriptor(method_id);
+ member_name_ = dex_file.GetMethodName(method_id);
+ type_signature_ = dex_file.GetMethodSignature(method_id).ToString();
+ type_ = kMethod;
+}
+
inline std::vector<const char*> MemberSignature::GetSignatureParts() const {
if (type_ == kField) {
return { class_name_.c_str(), "->", member_name_.c_str(), ":", type_signature_.c_str() };
@@ -137,6 +158,17 @@ void MemberSignature::WarnAboutAccess(AccessMethod access_method, hiddenapi::Api
<< Dumpable<MemberSignature>(*this) << " (" << list << ", " << access_method << ")";
}
+bool MemberSignature::Equals(const MemberSignature& other) {
+ return type_ == other.type_ &&
+ class_name_ == other.class_name_ &&
+ member_name_ == other.member_name_ &&
+ type_signature_ == other.type_signature_;
+}
+
+bool MemberSignature::MemberNameAndTypeMatch(const MemberSignature& other) {
+ return member_name_ == other.member_name_ && type_signature_ == other.type_signature_;
+}
+
#ifdef ART_TARGET_ANDROID
// Convert an AccessMethod enum to a value for logging from the proto enum.
// This method may look odd (the enum values are current the same), but it
@@ -238,63 +270,88 @@ static ALWAYS_INLINE void MaybeWhitelistMember(Runtime* runtime, T* member)
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;
- }
+static ALWAYS_INLINE uint32_t GetMemberDexIndex(ArtField* field) {
+ return field->GetDexFieldIndex();
+}
- uint32_t flags = kInvalidDexFlags;
- DCHECK(!AreValidDexFlags(flags));
+static ALWAYS_INLINE uint32_t GetMemberDexIndex(ArtMethod* method)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Use the non-obsolete method to avoid DexFile mismatch between
+ // the method index and the declaring class.
+ return method->GetNonObsoleteMethod()->GetDexMethodIndex();
+}
- 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();
- }
- };
+static void VisitMembers(const DexFile& dex_file,
+ const DexFile::ClassDef& class_def,
+ const std::function<void(const ClassAccessor::Field&)>& fn_visit) {
+ ClassAccessor accessor(dex_file, class_def, /* parse_hiddenapi_class_data= */ true);
accessor.VisitFields(fn_visit, fn_visit);
+}
- CHECK_NE(flags, kInvalidDexFlags) << "Could not find flags for field " << field->PrettyField();
- DCHECK(AreValidDexFlags(flags));
- return flags;
+static void VisitMembers(const DexFile& dex_file,
+ const DexFile::ClassDef& class_def,
+ const std::function<void(const ClassAccessor::Method&)>& fn_visit) {
+ ClassAccessor accessor(dex_file, class_def, /* parse_hiddenapi_class_data= */ true);
+ accessor.VisitMethods(fn_visit, fn_visit);
}
-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;
- }
+template<typename T>
+uint32_t GetDexFlags(T* member) REQUIRES_SHARED(Locks::mutator_lock_) {
+ static_assert(std::is_same<T, ArtField>::value || std::is_same<T, ArtMethod>::value);
+ using AccessorType = typename std::conditional<std::is_same<T, ArtField>::value,
+ ClassAccessor::Field, ClassAccessor::Method>::type;
- const DexFile::ClassDef* class_def = declaring_class->GetClassDef();
- if (class_def == nullptr) {
+ ObjPtr<mirror::Class> declaring_class = member->GetDeclaringClass();
+ if (declaring_class.IsNull()) {
return kNoDexFlags;
}
uint32_t flags = kInvalidDexFlags;
DCHECK(!AreValidDexFlags(flags));
- // Use the non-obsolete method to avoid DexFile mismatch between
- // the method index and the declaring class.
- uint32_t method_index = method->GetNonObsoleteMethod()->GetDexMethodIndex();
-
- 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_index) {
- flags = dex_method.GetHiddenapiFlags();
+ // Check if the declaring class has ClassExt allocated. If it does, check if
+ // the pre-JVMTI redefine dex file has been set to determine if the declaring
+ // class has been JVMTI-redefined.
+ ObjPtr<mirror::ClassExt> ext(declaring_class->GetExtData());
+ const DexFile* original_dex = ext.IsNull() ? nullptr : ext->GetPreRedefineDexFile();
+ if (LIKELY(original_dex == nullptr)) {
+ // Class is not redefined. Find the class def, iterate over its members and
+ // find the entry corresponding to this `member`.
+ const DexFile::ClassDef* class_def = declaring_class->GetClassDef();
+ if (class_def == nullptr) {
+ flags = kNoDexFlags;
+ } else {
+ uint32_t member_index = GetMemberDexIndex(member);
+ auto fn_visit = [&](const AccessorType& dex_member) {
+ if (dex_member.GetIndex() == member_index) {
+ flags = dex_member.GetHiddenapiFlags();
+ }
+ };
+ VisitMembers(declaring_class->GetDexFile(), *class_def, fn_visit);
}
- };
- accessor.VisitMethods(fn_visit, fn_visit);
+ } else {
+ // Class was redefined using JVMTI. We have a pointer to the original dex file
+ // and the class def index of this class in that dex file, but the field/method
+ // indices are lost. Iterate over all members of the class def and find the one
+ // corresponding to this `member` by name and type string comparison.
+ // This is obviously very slow, but it is only used when non-exempt code tries
+ // to access a hidden member of a JVMTI-redefined class.
+ uint16_t class_def_idx = ext->GetPreRedefineClassDefIndex();
+ DCHECK_NE(class_def_idx, DexFile::kDexNoIndex16);
+ const DexFile::ClassDef& original_class_def = original_dex->GetClassDef(class_def_idx);
+ MemberSignature member_signature(member);
+ auto fn_visit = [&](const AccessorType& dex_member) {
+ MemberSignature cur_signature(dex_member);
+ if (member_signature.MemberNameAndTypeMatch(cur_signature)) {
+ DCHECK(member_signature.Equals(cur_signature));
+ flags = dex_member.GetHiddenapiFlags();
+ }
+ };
+ VisitMembers(*original_dex, original_class_def, fn_visit);
+ }
- CHECK_NE(flags, kInvalidDexFlags) << "Could not find flags for method " << method->PrettyMethod();
+ CHECK_NE(flags, kInvalidDexFlags) << "Could not find hiddenapi flags for "
+ << Dumpable<MemberSignature>(MemberSignature(member));
DCHECK(AreValidDexFlags(flags));
return flags;
}
@@ -356,6 +413,8 @@ bool ShouldDenyAccessToMemberImpl(T* member,
}
// Need to instantiate this.
+template uint32_t GetDexFlags<ArtField>(ArtField* member);
+template uint32_t GetDexFlags<ArtMethod>(ArtMethod* member);
template bool ShouldDenyAccessToMemberImpl<ArtField>(ArtField* member,
hiddenapi::ApiList api_list,
AccessMethod access_method);
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index eea58e9880..614154c7a0 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -137,9 +137,14 @@ class MemberSignature {
public:
explicit MemberSignature(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_);
explicit MemberSignature(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+ explicit MemberSignature(const ClassAccessor::Field& field);
+ explicit MemberSignature(const ClassAccessor::Method& method);
void Dump(std::ostream& os) const;
+ bool Equals(const MemberSignature& other);
+ bool MemberNameAndTypeMatch(const MemberSignature& other);
+
// Performs prefix match on this member. Since the full member signature is
// composed of several parts, we match each part in turn (rather than
// building the entire thing in memory and performing a simple prefix match)
@@ -160,11 +165,8 @@ class MemberSignature {
// 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>
+uint32_t GetDexFlags(T* member) REQUIRES_SHARED(Locks::mutator_lock_);
template<typename T>
bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method)
diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc
index 67126307dc..146adc978c 100644
--- a/runtime/mirror/class_ext.cc
+++ b/runtime/mirror/class_ext.cc
@@ -119,5 +119,17 @@ void ClassExt::SetOriginalDexFile(ObjPtr<Object> bytes) {
SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_), bytes);
}
+void ClassExt::SetPreRedefineClassDefIndex(uint16_t index) {
+ DCHECK(!Runtime::Current()->IsActiveTransaction());
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ClassExt, pre_redefine_class_def_index_),
+ static_cast<int32_t>(index));
+}
+
+void ClassExt::SetPreRedefineDexFile(const DexFile* dex_file) {
+ DCHECK(!Runtime::Current()->IsActiveTransaction());
+ SetField64<false>(OFFSET_OF_OBJECT_MEMBER(ClassExt, pre_redefine_dex_file_ptr_),
+ static_cast<int64_t>(reinterpret_cast<uintptr_t>(dex_file)));
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/class_ext.h b/runtime/mirror/class_ext.h
index 612fd0f256..126f94a61d 100644
--- a/runtime/mirror/class_ext.h
+++ b/runtime/mirror/class_ext.h
@@ -64,6 +64,20 @@ class MANAGED ClassExt : public Object {
void SetOriginalDexFile(ObjPtr<Object> bytes) REQUIRES_SHARED(Locks::mutator_lock_);
+ uint16_t GetPreRedefineClassDefIndex() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return static_cast<uint16_t>(
+ GetField32(OFFSET_OF_OBJECT_MEMBER(ClassExt, pre_redefine_class_def_index_)));
+ }
+
+ void SetPreRedefineClassDefIndex(uint16_t index) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ const DexFile* GetPreRedefineDexFile() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(
+ GetField64(OFFSET_OF_OBJECT_MEMBER(ClassExt, pre_redefine_dex_file_ptr_))));
+ }
+
+ void SetPreRedefineDexFile(const DexFile* dex_file) REQUIRES_SHARED(Locks::mutator_lock_);
+
void SetObsoleteArrays(ObjPtr<PointerArray> methods, ObjPtr<ObjectArray<DexCache>> dex_caches)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -88,6 +102,10 @@ class MANAGED ClassExt : public Object {
// The saved verification error of this class.
HeapReference<Object> verify_error_;
+ // Native pointer to DexFile and ClassDef index of this class before it was JVMTI-redefined.
+ int64_t pre_redefine_dex_file_ptr_;
+ int32_t pre_redefine_class_def_index_;
+
friend struct art::ClassExtOffsets; // for verifying offset information
DISALLOW_IMPLICIT_CONSTRUCTORS(ClassExt);
};
diff --git a/test/999-redefine-hiddenapi/src-redefine/gen.sh b/test/999-redefine-hiddenapi/src-redefine/gen.sh
index b5e2aea0bb..f92d79784b 100755
--- a/test/999-redefine-hiddenapi/src-redefine/gen.sh
+++ b/test/999-redefine-hiddenapi/src-redefine/gen.sh
@@ -23,16 +23,10 @@ CLASS="art/Test999"
(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" \
- --api-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 4627b4fd22..3d9bbda801 100644
--- a/test/999-redefine-hiddenapi/src/Main.java
+++ b/test/999-redefine-hiddenapi/src/Main.java
@@ -31,53 +31,33 @@ 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(true, klass, "before redefinition");
- assertFieldIsHidden(true, klass, "before redefinition");
+ assertFieldIsHidden(klass, "before redefinition");
+ assertMethodIsHidden(klass, "before redefinition");
// 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 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");
+ // Verify that the class members are still hidden.
+ assertFieldIsHidden(klass, "after first redefinition");
+ assertMethodIsHidden(klass, "after first redefinition");
}
- private static void assertMethodIsHidden(boolean expectedHidden, Class<?> klass, String msg) {
+ private static void assertMethodIsHidden(Class<?> klass, String msg) {
try {
klass.getDeclaredMethod("foo");
- if (expectedHidden) {
- // Unexpected. Should have thrown NoSuchMethodException.
- throw new RuntimeException("Method should not be accessible " + msg);
- }
+ // Unexpected. Should have thrown NoSuchMethodException.
+ throw new RuntimeException("Method should not be accessible " + msg);
} catch (NoSuchMethodException ex) {
- if (!expectedHidden) {
- // Unexpected. Should not have thrown NoSuchMethodException.
- throw new RuntimeException("Method should be accessible " + msg);
- }
}
}
- private static void assertFieldIsHidden(boolean expectedHidden, Class<?> klass, String msg) {
+ private static void assertFieldIsHidden(Class<?> klass, String msg) {
try {
klass.getDeclaredField("bar");
- if (expectedHidden) {
- // Unexpected. Should have thrown NoSuchFieldException.
- throw new RuntimeException("Field should not be accessible " + msg);
- }
+ // Unexpected. Should have thrown NoSuchFieldException.
+ throw new RuntimeException("Field should not be accessible " + msg);
} catch (NoSuchFieldException ex) {
- if (!expectedHidden) {
- // Unexpected. Should not have thrown NoSuchFieldException.
- throw new RuntimeException("Field should be accessible " + msg);
- }
}
}
@@ -127,21 +107,4 @@ public class Main {
"AAAABAAAAAIAAADkAAAABQAAAAQAAAD0AAAABgAAAAEAAAAUAQAAASAAAAIAAAA0AQAAAyAAAAIA" +
"AAB0AQAAARAAAAEAAACAAQAAAiAAABAAAACGAQAAACAAAAEAAACgAgAAAxAAAAEAAACwAgAAABAA" +
"AAEAAAC0AgAA");
- private static final byte[] DEX_BYTES_HIDDEN = Base64.getDecoder().decode(
- "ZGV4CjAzNQDsgG5ufKulToQpDF+P4dsgeOkgfzzH+5l4AwAAcAAAAHhWNBIAAAAAAAAAAMACAAAQ" +
- "AAAAcAAAAAcAAACwAAAAAgAAAMwAAAACAAAA5AAAAAQAAAD0AAAAAQAAABQBAABEAgAANAEAAIYB" +
- "AACOAQAAlwEAAJoBAACpAQAAwAEAANQBAADoAQAA/AEAAAoCAAANAgAAEQIAABYCAAAbAgAAIAIA" +
- "ACkCAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAACAAQAA" +
- "AQAAAAsAAAAFAAIADQAAAAEAAAAAAAAAAQAAAAwAAAACAAEADgAAAAMAAAAAAAAAAQAAAAEAAAAD" +
- "AAAAAAAAAAgAAAAAAAAAoAIAAAAAAAACAAEAAQAAAHQBAAAIAAAAcBADAAEAEwBAAFkQAAAOAAMA" +
- "AQACAAAAeQEAAAgAAABiAAEAGgEBAG4gAgAQAA4AEwAOQAAVAA54AAAAAQAAAAQABjxpbml0PgAH" +
- "R29vZGJ5ZQABSQANTGFydC9UZXN0OTk5OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9s" +
- "YW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0" +
- "OTk5LmphdmEAAVYAAlZMAANiYXIAA2ZvbwADb3V0AAdwcmludGxuAHV+fkQ4eyJjb21waWxhdGlv" +
- "bi1tb2RlIjoiZGVidWciLCJtaW4tYXBpIjoxLCJzaGEtMSI6ImQyMmFiNGYxOWI3NTYxNDQ3NTI4" +
- "NTdjYTg2YjJjZWU0ZGQ5Y2ExNjYiLCJ2ZXJzaW9uIjoiMS40LjktZGV2In0AAAEBAQABAIGABLQC" +
- "AQHUAgAAAAALAAAACAAAAAIAAgAPAAAAAAAAAAEAAAAAAAAAAQAAABAAAABwAAAAAgAAAAcAAACw" +
- "AAAAAwAAAAIAAADMAAAABAAAAAIAAADkAAAABQAAAAQAAAD0AAAABgAAAAEAAAAUAQAAASAAAAIA" +
- "AAA0AQAAAyAAAAIAAAB0AQAAARAAAAEAAACAAQAAAiAAABAAAACGAQAAACAAAAEAAACgAgAAAxAA" +
- "AAEAAACwAgAAAPAAAAEAAAC0AgAAABAAAAEAAADAAgAA");
}