Runtime flags only for fast/slow hiddenapi path

With more flags being supported in the dex file, stop copying all of
them into ArtField/ArtMethod access flags. Instead, store the
information needed to figure out whether to enter the slow path and
retrieve full access flags from dex or not.

At the moment, the only runtime flag is kAccPublicApi assigned to all
class members on the whitelist.

The CL also moves hardcoded API membership of intrinsics out of
ArtMethod and into hidden_api.h, and moves ArtMethod::SetIntrinsic
into the .cc file.

Test: m test-art
Change-Id: Ia1cc05060dbc22341768161dfd8697c6158e803a
diff --git a/libdexfile/dex/hidden_api_access_flags.h b/libdexfile/dex/hidden_api_access_flags.h
index fd5c865..77bfbc9 100644
--- a/libdexfile/dex/hidden_api_access_flags.h
+++ b/libdexfile/dex/hidden_api_access_flags.h
@@ -48,27 +48,6 @@
   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 c4ea2d3..114c8e6 100644
--- a/libdexfile/dex/modifiers.h
+++ b/libdexfile/dex/modifiers.h
@@ -52,7 +52,7 @@
 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 @@
 // 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 @@
 
 // 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 7cd1039..cabb758 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -1427,6 +1427,11 @@
     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 @@
       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 dc7f985..1cf7afa 100644
--- a/runtime/art_field.h
+++ b/runtime/art_field.h
@@ -180,10 +180,6 @@
     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 d8da912..f254116 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -367,160 +367,6 @@
   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 9bf31ed..abfdd55 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 @@
   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 4e3ef53..5bbee92 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -343,8 +343,6 @@
     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 35379cc..ce7dfaf 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3468,14 +3468,8 @@
   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 @@
   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/hidden_api.cc b/runtime/hidden_api.cc
index 3b7b938..ab9e65c 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -18,8 +18,12 @@
 
 #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 "dex/class_accessor-inl.h"
+#include "scoped_thread_state_change.h"
+#include "thread-inl.h"
 #include "well_known_classes.h"
 
 #ifdef ART_TARGET_ANDROID
@@ -235,23 +239,82 @@
   }
 }
 
-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,
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index ed00e2a..98414f7 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"
@@ -164,12 +165,136 @@
   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 +308,9 @@
     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 +320,11 @@
     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/mirror/class.h b/runtime/mirror/class.h
index c38cc86..4e551ad 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -211,12 +211,12 @@
   }
 
   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/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc
index 8e3e4eb..c4439de 100644
--- a/test/674-hiddenapi/hiddenapi.cc
+++ b/test/674-hiddenapi/hiddenapi.cc
@@ -284,7 +284,7 @@
 }
 
 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 6948cbb..f78a025 100755
--- a/test/999-redefine-hiddenapi/src-redefine/gen.sh
+++ b/test/999-redefine-hiddenapi/src-redefine/gen.sh
@@ -18,13 +18,21 @@
 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 c6365ac..4627b4f 100644
--- a/test/999-redefine-hiddenapi/src/Main.java
+++ b/test/999-redefine-hiddenapi/src/Main.java
@@ -19,7 +19,7 @@
 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 @@
 
     // 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 @@
     "ASoQQLUAArEAAAABAA0AAAAKAAIAAAATAAQAGAABAA4ACwABAAwAAAAlAAIAAQAAAAmyAAMSBLYA" +
     "BbEAAAABAA0AAAAKAAIAAAAVAAgAFgABAA8AAAACABA=");
   private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-    "ZGV4CjAzNQD0dZ+IWxOi+cJDSWjfTnUerlZj1Lll3ONIAwAAcAAAAHhWNBIAAAAAAAAAAJwCAAAQ" +
-    "AAAAcAAAAAcAAACwAAAAAgAAAMwAAAACAAAA5AAAAAQAAAD0AAAAAQAAABQBAAAUAgAANAEAAIYB" +
+    "ZGV4CjAzNQDlfmgFfKulToQpDF+P4dsgeOkgfzzH+5lgAwAAcAAAAHhWNBIAAAAAAAAAALQCAAAQ" +
+    "AAAAcAAAAAcAAACwAAAAAgAAAMwAAAACAAAA5AAAAAQAAAD0AAAAAQAAABQBAAAsAgAANAEAAIYB" +
     "AACOAQAAlwEAAJoBAACpAQAAwAEAANQBAADoAQAA/AEAAAoCAAANAgAAEQIAABYCAAAbAgAAIAIA" +
     "ACkCAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAACAAQAA" +
     "AQAAAAsAAAAFAAIADQAAAAEAAAAAAAAAAQAAAAwAAAACAAEADgAAAAMAAAAAAAAAAQAAAAEAAAAD" +
-    "AAAAAAAAAAgAAAAAAAAAhwIAAAAAAAACAAEAAQAAAHQBAAAIAAAAcBADAAEAEwBAAFkQAAAOAAMA" +
+    "AAAAAAAAAAgAAAAAAAAAoAIAAAAAAAACAAEAAQAAAHQBAAAIAAAAcBADAAEAEwBAAFkQAAAOAAMA" +
     "AQACAAAAeQEAAAgAAABiAAEAGgEBAG4gAgAQAA4AEwAOQAAVAA54AAAAAQAAAAQABjxpbml0PgAH" +
     "R29vZGJ5ZQABSQANTGFydC9UZXN0OTk5OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9s" +
     "YW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0" +
-    "OTk5LmphdmEAAVYAAlZMAANiYXIAA2ZvbwADb3V0AAdwcmludGxuAFx+fkQ4eyJtaW4tYXBpIjox" +
-    "LCJzaGEtMSI6IjU2YzJlMzBmNTIzM2I4NDRmZjZkZGQ4N2ZiNTNkMzRmYjE3MjM3ZGYiLCJ2ZXJz" +
-    "aW9uIjoidjEuMi4xNS1kZXYifQAAAQEBAAEAgYAEtAIBAdQCAAAAAAAOAAAAAAAAAAEAAAAAAAAA" +
-    "AQAAABAAAABwAAAAAgAAAAcAAACwAAAAAwAAAAIAAADMAAAABAAAAAIAAADkAAAABQAAAAQAAAD0" +
-    "AAAABgAAAAEAAAAUAQAAASAAAAIAAAA0AQAAAyAAAAIAAAB0AQAAARAAAAEAAACAAQAAAiAAABAA" +
-    "AACGAQAAACAAAAEAAACHAgAAAxAAAAEAAACYAgAAABAAAAEAAACcAgAA");
+    "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" +
+    "AAAAAAAAAAgAAAAAAAAAoAIAAAAAAAACAAEAAQAAAHQBAAAIAAAAcBADAAEAEwBAAFkQAAAOAAMA" +
+    "AQACAAAAeQEAAAgAAABiAAEAGgEBAG4gAgAQAA4AEwAOQAAVAA54AAAAAQAAAAQABjxpbml0PgAH" +
+    "R29vZGJ5ZQABSQANTGFydC9UZXN0OTk5OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9s" +
+    "YW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0" +
+    "OTk5LmphdmEAAVYAAlZMAANiYXIAA2ZvbwADb3V0AAdwcmludGxuAHV+fkQ4eyJjb21waWxhdGlv" +
+    "bi1tb2RlIjoiZGVidWciLCJtaW4tYXBpIjoxLCJzaGEtMSI6ImQyMmFiNGYxOWI3NTYxNDQ3NTI4" +
+    "NTdjYTg2YjJjZWU0ZGQ5Y2ExNjYiLCJ2ZXJzaW9uIjoiMS40LjktZGV2In0AAAEBAQABAIGABLQC" +
+    "AQHUAgAAAAALAAAACAAAAAIAAgAPAAAAAAAAAAEAAAAAAAAAAQAAABAAAABwAAAAAgAAAAcAAACw" +
+    "AAAAAwAAAAIAAADMAAAABAAAAAIAAADkAAAABQAAAAQAAAD0AAAABgAAAAEAAAAUAQAAASAAAAIA" +
+    "AAA0AQAAAyAAAAIAAAB0AQAAARAAAAEAAACAAQAAAiAAABAAAACGAQAAACAAAAEAAACgAgAAAxAA" +
+    "AAEAAACwAgAAAPAAAAEAAAC0AgAAABAAAAEAAADAAgAA");
 }