Keep pointer to original DexFile during JVMTI redefine for hiddenapi

JVMTI redefine overwrites the pointer to the class' DexFile which
prevents access checks from reading the hiddenapi flags store. Store
the pointer in ClassExt together with the original ClassDef index
to preserve the access to flags store. Because method/field indices
are still lost, the corresponding dex member is found using string
comparison of member's name and type.

Bug: 119688837
Test: 999-redefine-hiddenapi
Change-Id: Ifdf35668e838869a971233bbaae61851014658b1
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index cabb758..6ca4e38 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -1427,11 +1427,6 @@
     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 @@
       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 @@
   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 56fdd06..fe45b9e 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -611,6 +611,10 @@
     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 6cdba73..e0939dd 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 @@
   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 @@
                << 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 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(!AreValidDexFlags(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(AreValidDexFlags(flags));
-  return flags;
+static ALWAYS_INLINE uint32_t GetMemberDexIndex(ArtField* field) {
+  return field->GetDexFieldIndex();
 }
 
-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;
-  }
+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();
+}
 
-  const DexFile::ClassDef* class_def = declaring_class->GetClassDef();
-  if (class_def == nullptr) {
+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);
+}
+
+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);
+}
+
+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;
+
+  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 @@
 }
 
 // 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 eea58e9..614154c 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -137,9 +137,14 @@
  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 @@
 
 // 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 6712630..146adc9 100644
--- a/runtime/mirror/class_ext.cc
+++ b/runtime/mirror/class_ext.cc
@@ -119,5 +119,17 @@
   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 612fd0f..126f94a 100644
--- a/runtime/mirror/class_ext.h
+++ b/runtime/mirror/class_ext.h
@@ -64,6 +64,20 @@
 
   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 @@
   // 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 b5e2aea..f92d797 100755
--- a/test/999-redefine-hiddenapi/src-redefine/gen.sh
+++ b/test/999-redefine-hiddenapi/src-redefine/gen.sh
@@ -23,16 +23,10 @@
 (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 4627b4f..3d9bbda 100644
--- a/test/999-redefine-hiddenapi/src/Main.java
+++ b/test/999-redefine-hiddenapi/src/Main.java
@@ -31,53 +31,33 @@
 
     // 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 @@
     "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");
 }