Merge "Handle multidex in LocationIsOnSystemFramework." into pi-dev
diff --git a/libdexfile/dex/modifiers.h b/libdexfile/dex/modifiers.h
index 2425a58..be82fff 100644
--- a/libdexfile/dex/modifiers.h
+++ b/libdexfile/dex/modifiers.h
@@ -58,6 +58,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)
 // 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_
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 65c6406..8e21fd3 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -143,31 +143,45 @@
 // Returns true if the caller is either loaded by the boot strap class loader or comes from
 // a dex file located in ${ANDROID_ROOT}/framework/.
 ALWAYS_INLINE
-inline bool IsCallerInPlatformDex(ObjPtr<mirror::ClassLoader> caller_class_loader,
-                                  ObjPtr<mirror::DexCache> caller_dex_cache)
+inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller,
+                            ObjPtr<mirror::ClassLoader> caller_class_loader,
+                            ObjPtr<mirror::DexCache> caller_dex_cache)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   if (caller_class_loader.IsNull()) {
+    // Boot class loader.
     return true;
-  } else if (caller_dex_cache.IsNull()) {
-    return false;
-  } else {
-    const DexFile* caller_dex_file = caller_dex_cache->GetDexFile();
-    return caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile();
   }
+
+  if (!caller_dex_cache.IsNull()) {
+    const DexFile* caller_dex_file = caller_dex_cache->GetDexFile();
+    if (caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile()) {
+      // Caller is in a platform dex file.
+      return true;
+    }
+  }
+
+  if (!caller.IsNull() &&
+      caller->ShouldSkipHiddenApiChecks() &&
+      Runtime::Current()->IsJavaDebuggable()) {
+    // We are in debuggable mode and this caller has been marked trusted.
+    return true;
+  }
+
+  return false;
 }
 
 }  // namespace detail
 
 // Returns true if access to `member` should be denied to the caller of the
-// reflective query. The decision is based on whether the caller is in the
-// platform or not. Because different users of this function determine this
-// in a different way, `fn_caller_in_platform(self)` is called and should
-// return true if the caller is located in the platform.
+// reflective query. The decision is based on whether the caller is trusted or
+// not. Because different users of this function determine this in a different
+// way, `fn_caller_is_trusted(self)` is called and should return true if the
+// caller is allowed to access the platform.
 // This function might print warnings into the log if the member is hidden.
 template<typename T>
 inline Action GetMemberAction(T* member,
                               Thread* self,
-                              std::function<bool(Thread*)> fn_caller_in_platform,
+                              std::function<bool(Thread*)> fn_caller_is_trusted,
                               AccessMethod access_method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(member != nullptr);
@@ -188,8 +202,8 @@
 
   // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access.
   // This can be *very* expensive. Save it for last.
-  if (fn_caller_in_platform(self)) {
-    // Caller in the platform. Exit.
+  if (fn_caller_is_trusted(self)) {
+    // Caller is trusted. Exit.
     return kAllow;
   }
 
@@ -197,10 +211,9 @@
   return detail::GetMemberActionImpl(member, api_list, action, access_method);
 }
 
-inline bool IsCallerInPlatformDex(ObjPtr<mirror::Class> caller)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
+inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller) REQUIRES_SHARED(Locks::mutator_lock_) {
   return !caller.IsNull() &&
-      detail::IsCallerInPlatformDex(caller->GetClassLoader(), caller->GetDexCache());
+      detail::IsCallerTrusted(caller, caller->GetClassLoader(), caller->GetDexCache());
 }
 
 // Returns true if access to `member` should be denied to a caller loaded with
@@ -212,10 +225,11 @@
                               ObjPtr<mirror::DexCache> caller_dex_cache,
                               AccessMethod access_method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  bool caller_in_platform = detail::IsCallerInPlatformDex(caller_class_loader, caller_dex_cache);
+  bool is_caller_trusted =
+      detail::IsCallerTrusted(/* caller */ nullptr, caller_class_loader, caller_dex_cache);
   return GetMemberAction(member,
                          /* thread */ nullptr,
-                         [caller_in_platform] (Thread*) { return caller_in_platform; },
+                         [is_caller_trusted] (Thread*) { return is_caller_trusted; },
                          access_method);
 }
 
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index a757960..57d3b9f 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -183,10 +183,12 @@
   if (jit_compiler_handle_ == nullptr && !LoadCompiler(error_msg)) {
     return nullptr;
   }
+  bool code_cache_only_for_profile_data = !options->UseJitCompilation();
   jit->code_cache_.reset(JitCodeCache::Create(
       options->GetCodeCacheInitialCapacity(),
       options->GetCodeCacheMaxCapacity(),
       jit->generate_debug_info_,
+      code_cache_only_for_profile_data,
       error_msg));
   if (jit->GetCodeCache() == nullptr) {
     return nullptr;
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index f0f0df4..1048c80 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -50,7 +50,6 @@
 namespace art {
 namespace jit {
 
-static constexpr int kProtAll = PROT_READ | PROT_WRITE | PROT_EXEC;
 static constexpr int kProtData = PROT_READ | PROT_WRITE;
 static constexpr int kProtCode = PROT_READ | PROT_EXEC;
 
@@ -161,6 +160,7 @@
 JitCodeCache* JitCodeCache::Create(size_t initial_capacity,
                                    size_t max_capacity,
                                    bool generate_debug_info,
+                                   bool used_only_for_profile_data,
                                    std::string* error_msg) {
   ScopedTrace trace(__PRETTY_FUNCTION__);
   CHECK_GE(max_capacity, initial_capacity);
@@ -184,6 +184,15 @@
     return nullptr;
   }
 
+  // Decide how we should map the code and data sections.
+  // If we use the code cache just for profiling we do not need to map the code section as
+  // executable.
+  // NOTE 1: this is yet another workaround to bypass strict SElinux policies in order to be able
+  //         to profile system server.
+  // NOTE 2: We could just not create the code section at all but we will need to
+  //         special case too many cases.
+  int memmap_flags_prot_code = used_only_for_profile_data ? (kProtCode & ~PROT_EXEC) : kProtCode;
+
   std::string error_str;
   // Map name specific for android_os_Debug.cpp accounting.
   // Map in low 4gb to simplify accessing root tables for x86_64.
@@ -216,8 +225,11 @@
   DCHECK_EQ(code_size + data_size, max_capacity);
   uint8_t* divider = data_map->Begin() + data_size;
 
-  MemMap* code_map =
-      data_map->RemapAtEnd(divider, "jit-code-cache", kProtAll, &error_str, use_ashmem);
+  MemMap* code_map = data_map->RemapAtEnd(
+      divider,
+      "jit-code-cache",
+      memmap_flags_prot_code | PROT_WRITE,
+      &error_str, use_ashmem);
   if (code_map == nullptr) {
     std::ostringstream oss;
     oss << "Failed to create read write execute cache: " << error_str << " size=" << max_capacity;
@@ -229,7 +241,13 @@
   code_size = initial_capacity - data_size;
   DCHECK_EQ(code_size + data_size, initial_capacity);
   return new JitCodeCache(
-      code_map, data_map.release(), code_size, data_size, max_capacity, garbage_collect_code);
+      code_map,
+      data_map.release(),
+      code_size,
+      data_size,
+      max_capacity,
+      garbage_collect_code,
+      memmap_flags_prot_code);
 }
 
 JitCodeCache::JitCodeCache(MemMap* code_map,
@@ -237,7 +255,8 @@
                            size_t initial_code_capacity,
                            size_t initial_data_capacity,
                            size_t max_capacity,
-                           bool garbage_collect_code)
+                           bool garbage_collect_code,
+                           int memmap_flags_prot_code)
     : lock_("Jit code cache", kJitCodeCacheLock),
       lock_cond_("Jit code cache condition variable", lock_),
       collection_in_progress_(false),
@@ -259,7 +278,8 @@
       histogram_code_memory_use_("Memory used for compiled code", 16),
       histogram_profiling_info_memory_use_("Memory used for profiling info", 16),
       is_weak_access_enabled_(true),
-      inline_cache_cond_("Jit inline cache condition variable", lock_) {
+      inline_cache_cond_("Jit inline cache condition variable", lock_),
+      memmap_flags_prot_code_(memmap_flags_prot_code) {
 
   DCHECK_GE(max_capacity, initial_code_capacity + initial_data_capacity);
   code_mspace_ = create_mspace_with_base(code_map_->Begin(), code_end_, false /*locked*/);
@@ -275,7 +295,7 @@
               "mprotect jit code cache",
               code_map_->Begin(),
               code_map_->Size(),
-              kProtCode);
+              memmap_flags_prot_code_);
   CheckedCall(mprotect,
               "mprotect jit data cache",
               data_map_->Begin(),
@@ -328,28 +348,32 @@
 
 class ScopedCodeCacheWrite : ScopedTrace {
  public:
-  explicit ScopedCodeCacheWrite(MemMap* code_map, bool only_for_tlb_shootdown = false)
+  explicit ScopedCodeCacheWrite(const JitCodeCache* const code_cache,
+                                bool only_for_tlb_shootdown = false)
       : ScopedTrace("ScopedCodeCacheWrite"),
-        code_map_(code_map),
+        code_cache_(code_cache),
         only_for_tlb_shootdown_(only_for_tlb_shootdown) {
     ScopedTrace trace("mprotect all");
-    CheckedCall(mprotect,
-                "make code writable",
-                code_map_->Begin(),
-                only_for_tlb_shootdown_ ? kPageSize : code_map_->Size(),
-                kProtAll);
+    CheckedCall(
+        mprotect,
+        "make code writable",
+        code_cache_->code_map_->Begin(),
+        only_for_tlb_shootdown_ ? kPageSize : code_cache_->code_map_->Size(),
+        code_cache_->memmap_flags_prot_code_ | PROT_WRITE);
   }
+
   ~ScopedCodeCacheWrite() {
     ScopedTrace trace("mprotect code");
-    CheckedCall(mprotect,
-                "make code protected",
-                code_map_->Begin(),
-                only_for_tlb_shootdown_ ? kPageSize : code_map_->Size(),
-                kProtCode);
+    CheckedCall(
+        mprotect,
+        "make code protected",
+        code_cache_->code_map_->Begin(),
+        only_for_tlb_shootdown_ ? kPageSize : code_cache_->code_map_->Size(),
+        code_cache_->memmap_flags_prot_code_);
   }
 
  private:
-  MemMap* const code_map_;
+  const JitCodeCache* const code_cache_;
 
   // If we're using ScopedCacheWrite only for TLB shootdown, we limit the scope of mprotect to
   // one page.
@@ -571,7 +595,7 @@
   // so it's possible for the same method_header to start representing
   // different compile code.
   MutexLock mu(Thread::Current(), lock_);
-  ScopedCodeCacheWrite scc(code_map_.get());
+  ScopedCodeCacheWrite scc(this);
   for (const OatQuickMethodHeader* method_header : method_headers) {
     FreeCode(method_header->GetCode());
   }
@@ -590,7 +614,7 @@
     // with the classlinker_classes_lock_ held, and suspending ourselves could
     // lead to a deadlock.
     {
-      ScopedCodeCacheWrite scc(code_map_.get());
+      ScopedCodeCacheWrite scc(this);
       for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) {
         it->second.RemoveMethodsIn(alloc);
         if (it->second.GetMethods().empty()) {
@@ -729,7 +753,7 @@
     MutexLock mu(self, lock_);
     WaitForPotentialCollectionToComplete(self);
     {
-      ScopedCodeCacheWrite scc(code_map_.get());
+      ScopedCodeCacheWrite scc(this);
       memory = AllocateCode(total_size);
       if (memory == nullptr) {
         return nullptr;
@@ -817,7 +841,7 @@
       {
         // Flush data cache, as compiled code references literals in it.
         // We also need a TLB shootdown to act as memory barrier across cores.
-        ScopedCodeCacheWrite ccw(code_map_.get(), /* only_for_tlb_shootdown */ true);
+        ScopedCodeCacheWrite ccw(this, /* only_for_tlb_shootdown */ true);
         FlushDataCache(reinterpret_cast<char*>(roots_data),
                        reinterpret_cast<char*>(roots_data + data_size));
       }
@@ -895,7 +919,7 @@
   }
 
   bool in_cache = false;
-  ScopedCodeCacheWrite ccw(code_map_.get());
+  ScopedCodeCacheWrite ccw(this);
   if (UNLIKELY(method->IsNative())) {
     auto it = jni_stubs_map_.find(JniStubKey(method));
     if (it != jni_stubs_map_.end() && it->second.RemoveMethod(method)) {
@@ -1122,7 +1146,7 @@
   DCHECK_EQ(per_space_footprint * 2, new_footprint);
   mspace_set_footprint_limit(data_mspace_, per_space_footprint);
   {
-    ScopedCodeCacheWrite scc(code_map_.get());
+    ScopedCodeCacheWrite scc(this);
     mspace_set_footprint_limit(code_mspace_, per_space_footprint);
   }
 }
@@ -1290,7 +1314,7 @@
   std::unordered_set<OatQuickMethodHeader*> method_headers;
   {
     MutexLock mu(self, lock_);
-    ScopedCodeCacheWrite scc(code_map_.get());
+    ScopedCodeCacheWrite scc(this);
     // Iterate over all compiled code and remove entries that are not marked.
     for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) {
       JniStubData* data = &it->second;
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index dfa7ac0..92deb97 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -68,6 +68,7 @@
 namespace jit {
 
 class JitInstrumentationCache;
+class ScopedCodeCacheWrite;
 
 // Alignment in bits that will suit all architectures.
 static constexpr int kJitCodeAlignment = 16;
@@ -88,6 +89,7 @@
   static JitCodeCache* Create(size_t initial_capacity,
                               size_t max_capacity,
                               bool generate_debug_info,
+                              bool used_only_for_profile_data,
                               std::string* error_msg);
   ~JitCodeCache();
 
@@ -270,7 +272,8 @@
                size_t initial_code_capacity,
                size_t initial_data_capacity,
                size_t max_capacity,
-               bool garbage_collect_code);
+               bool garbage_collect_code,
+               int memmap_flags_prot_code);
 
   // Internal version of 'CommitCode' that will not retry if the
   // allocation fails. Return null if the allocation fails.
@@ -446,7 +449,12 @@
   // Condition to wait on for accessing inline caches.
   ConditionVariable inline_cache_cond_ GUARDED_BY(lock_);
 
+  // Mapping flags for the code section.
+  const int memmap_flags_prot_code_;
+
   friend class art::JitJniStubTestHelper;
+  friend class ScopedCodeCacheWrite;
+
   DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache);
 };
 
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index a473f3f..1208173 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -98,9 +98,7 @@
 
 ProfileCompilationInfo::~ProfileCompilationInfo() {
   VLOG(profiler) << Dumpable<MemStats>(allocator_.GetMemStats());
-  for (DexFileData* data : info_) {
-    delete data;
-  }
+  ClearData();
 }
 
 void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx,
@@ -2107,4 +2105,12 @@
   return true;
 }
 
+void ProfileCompilationInfo::ClearData() {
+  for (DexFileData* data : info_) {
+    delete data;
+  }
+  info_.clear();
+  profile_key_map_.clear();
+}
+
 }  // namespace art
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index 5c4b9e7..90204cd 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -442,6 +442,9 @@
   // Checks if the profile is empty.
   bool IsEmpty() const;
 
+  // Clears all the data from the profile.
+  void ClearData();
+
  private:
   enum ProfileLoadStatus {
     kProfileLoadWouldOverwiteData,
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index 0ebadc0..7e12dde 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -1339,4 +1339,33 @@
   ASSERT_TRUE(loaded_info.Equals(expected_info));
 }
 
+
+TEST_F(ProfileCompilationInfoTest, ClearData) {
+  ProfileCompilationInfo info;
+  for (uint16_t i = 0; i < 10; i++) {
+    ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &info));
+  }
+  ASSERT_FALSE(IsEmpty(info));
+  info.ClearData();
+  ASSERT_TRUE(IsEmpty(info));
+}
+
+TEST_F(ProfileCompilationInfoTest, ClearDataAndSave) {
+  ProfileCompilationInfo info;
+  for (uint16_t i = 0; i < 10; i++) {
+    ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &info));
+  }
+  info.ClearData();
+
+  ScratchFile profile;
+  ASSERT_TRUE(info.Save(GetFd(profile)));
+  ASSERT_EQ(0, profile.GetFile()->Flush());
+
+  // Check that we get back what we saved.
+  ProfileCompilationInfo loaded_info;
+  ASSERT_TRUE(profile.GetFile()->ResetOffset());
+  ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
+  ASSERT_TRUE(loaded_info.Equals(info));
+}
+
 }  // namespace art
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index fe4b88d..5ca5a7c 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -523,10 +523,24 @@
       uint64_t last_save_number_of_methods = info.GetNumberOfMethods();
       uint64_t last_save_number_of_classes = info.GetNumberOfResolvedClasses();
 
-      info.AddMethods(profile_methods, ProfileCompilationInfo::MethodHotness::kFlagPostStartup);
+      // Try to add the method data. Note this may fail is the profile loaded from disk contains
+      // outdated data (e.g. the previous profiled dex files might have been updated).
+      // If this happens we clear the profile data and for the save to ensure the file is cleared.
+      if (!info.AddMethods(profile_methods,
+              ProfileCompilationInfo::MethodHotness::kFlagPostStartup)) {
+        LOG(WARNING) << "Could not add methods to the existing profiler. "
+            << "Clearing the profile data.";
+        info.ClearData();
+        force_save = true;
+      }
+
       auto profile_cache_it = profile_cache_.find(filename);
       if (profile_cache_it != profile_cache_.end()) {
-        info.MergeWith(*(profile_cache_it->second));
+        if (!info.MergeWith(*(profile_cache_it->second))) {
+          LOG(WARNING) << "Could not merge the profile. Clearing the profile data.";
+          info.ClearData();
+          force_save = true;
+        }
       }
 
       int64_t delta_number_of_methods =
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 9dbcded..cd66a60 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -80,15 +80,15 @@
 // things not rendering correctly. E.g. b/16858794
 static constexpr bool kWarnJniAbort = false;
 
-static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
-  return hiddenapi::IsCallerInPlatformDex(GetCallingClass(self, /* num_frames */ 1));
+static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
+  return hiddenapi::IsCallerTrusted(GetCallingClass(self, /* num_frames */ 1));
 }
 
 template<typename T>
 ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   hiddenapi::Action action = hiddenapi::GetMemberAction(
-      member, self, IsCallerInPlatformDex, hiddenapi::kJNI);
+      member, self, IsCallerTrusted, hiddenapi::kJNI);
   if (action != hiddenapi::kAllow) {
     hiddenapi::NotifyHiddenApiListener(member);
   }
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 51d1376..98e25eb 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -210,6 +210,15 @@
     return (GetAccessFlags() & kAccClassIsFinalizable) != 0;
   }
 
+  ALWAYS_INLINE bool ShouldSkipHiddenApiChecks() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return (GetAccessFlags() & kAccSkipHiddenApiChecks) != 0;
+  }
+
+  ALWAYS_INLINE void SetSkipHiddenApiChecks() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint32_t flags = GetAccessFlags();
+    SetAccessFlags(flags | kAccSkipHiddenApiChecks);
+  }
+
   ALWAYS_INLINE void SetRecursivelyInitialized() REQUIRES_SHARED(Locks::mutator_lock_) {
     DCHECK_EQ(GetLockOwnerThreadId(), Thread::Current()->GetThreadId());
     uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_));
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 13319c4..637e7fa 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -816,6 +816,28 @@
   return static_cast<jlong>(file_size);
 }
 
+static void DexFile_setTrusted(JNIEnv* env, jclass, jobject j_cookie) {
+  Runtime* runtime = Runtime::Current();
+  ScopedObjectAccess soa(env);
+
+  // Currently only allow this for debuggable apps.
+  if (!runtime->IsJavaDebuggable()) {
+    ThrowSecurityException("Can't exempt class, process is not debuggable.");
+    return;
+  }
+
+  std::vector<const DexFile*> dex_files;
+  const OatFile* oat_file;
+  if (!ConvertJavaArrayToDexFiles(env, j_cookie, dex_files, oat_file)) {
+    Thread::Current()->AssertPendingException();
+    return;
+  }
+
+  for (const DexFile* dex_file : dex_files) {
+    const_cast<DexFile*>(dex_file)->SetIsPlatformDexFile();
+  }
+}
+
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"),
   NATIVE_METHOD(DexFile,
@@ -854,7 +876,8 @@
                 "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
   NATIVE_METHOD(DexFile, getStaticSizeOfDexFile, "(Ljava/lang/Object;)J"),
   NATIVE_METHOD(DexFile, getDexFileOptimizationStatus,
-                "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;")
+                "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
+  NATIVE_METHOD(DexFile, setTrusted, "(Ljava/lang/Object;)V")
 };
 
 void register_dalvik_system_DexFile(JNIEnv* env) {
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index fc94266..0955e86 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -573,6 +573,25 @@
   Runtime::Current()->AttachAgent(env, filename, classloader);
 }
 
+static void VMDebug_allowHiddenApiReflectionFrom(JNIEnv* env, jclass, jclass j_caller) {
+  Runtime* runtime = Runtime::Current();
+  ScopedObjectAccess soa(env);
+
+  if (!runtime->IsJavaDebuggable()) {
+    ThrowSecurityException("Can't exempt class, process is not debuggable.");
+    return;
+  }
+
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::Class> h_caller(hs.NewHandle(soa.Decode<mirror::Class>(j_caller)));
+  if (h_caller.IsNull()) {
+    ThrowNullPointerException("argument is null");
+    return;
+  }
+
+  h_caller->SetSkipHiddenApiChecks();
+}
+
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"),
   NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"),
@@ -608,6 +627,7 @@
   NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"),
   NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"),
   NATIVE_METHOD(VMDebug, nativeAttachAgent, "(Ljava/lang/String;Ljava/lang/ClassLoader;)V"),
+  NATIVE_METHOD(VMDebug, allowHiddenApiReflectionFrom, "(Ljava/lang/Class;)V"),
 };
 
 void register_dalvik_system_VMDebug(JNIEnv* env) {
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 3dd3e43..891cdfa 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -177,6 +177,7 @@
   DEBUG_GENERATE_MINI_DEBUG_INFO     = 1 << 11,
   HIDDEN_API_ENFORCEMENT_POLICY_MASK = (1 << 12)
                                      | (1 << 13),
+  PROFILE_SYSTEM_SERVER              = 1 << 14,
 
   // bits to shift (flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) by to get a value
   // corresponding to hiddenapi::EnforcementPolicy
@@ -308,6 +309,9 @@
       (runtime_flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) >> API_ENFORCEMENT_POLICY_SHIFT);
   runtime_flags &= ~HIDDEN_API_ENFORCEMENT_POLICY_MASK;
 
+  bool profile_system_server = (runtime_flags & PROFILE_SYSTEM_SERVER) == PROFILE_SYSTEM_SERVER;
+  runtime_flags &= ~PROFILE_SYSTEM_SERVER;
+
   if (runtime_flags != 0) {
     LOG(ERROR) << StringPrintf("Unknown bits set in runtime_flags: %#x", runtime_flags);
   }
@@ -391,7 +395,11 @@
         env, is_system_server, action, isa_string.c_str());
   } else {
     Runtime::Current()->InitNonZygoteOrPostFork(
-        env, is_system_server, Runtime::NativeBridgeAction::kUnload, nullptr);
+        env,
+        is_system_server,
+        Runtime::NativeBridgeAction::kUnload,
+        /*isa*/ nullptr,
+        profile_system_server);
   }
 }
 
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index e161ed4..c07c32a 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -52,7 +52,7 @@
 
 // Returns true if the first caller outside of the Class class or java.lang.invoke package
 // is in a platform DEX file.
-static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
+static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
   // Walk the stack and find the first frame not from java.lang.Class and not from java.lang.invoke.
   // This is very expensive. Save this till the last.
   struct FirstExternalCallerVisitor : public StackVisitor {
@@ -99,7 +99,7 @@
   FirstExternalCallerVisitor visitor(self);
   visitor.WalkStack();
   return visitor.caller != nullptr &&
-         hiddenapi::IsCallerInPlatformDex(visitor.caller->GetDeclaringClass());
+         hiddenapi::IsCallerTrusted(visitor.caller->GetDeclaringClass());
 }
 
 // Returns true if the first non-ClassClass caller up the stack is not allowed to
@@ -107,7 +107,7 @@
 ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
-  return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInPlatformDex(self);
+  return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerTrusted(self);
 }
 
 // Returns true if the first non-ClassClass caller up the stack should not be
@@ -116,7 +116,7 @@
 ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   hiddenapi::Action action = hiddenapi::GetMemberAction(
-      member, self, IsCallerInPlatformDex, hiddenapi::kReflection);
+      member, self, IsCallerTrusted, hiddenapi::kReflection);
   if (action != hiddenapi::kAllow) {
     hiddenapi::NotifyHiddenApiListener(member);
   }
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 891bb8a..4062d17 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -873,7 +873,11 @@
 }
 
 void Runtime::InitNonZygoteOrPostFork(
-    JNIEnv* env, bool is_system_server, NativeBridgeAction action, const char* isa) {
+    JNIEnv* env,
+    bool is_system_server,
+    NativeBridgeAction action,
+    const char* isa,
+    bool profile_system_server) {
   is_zygote_ = false;
 
   if (is_native_bridge_loaded_) {
@@ -896,8 +900,15 @@
   heap_->ResetGcPerformanceInfo();
 
   // We may want to collect profiling samples for system server, but we never want to JIT there.
-  if ((!is_system_server || !jit_options_->UseJitCompilation()) &&
-      !safe_mode_ &&
+  if (is_system_server) {
+    jit_options_->SetUseJitCompilation(false);
+    jit_options_->SetSaveProfilingInfo(profile_system_server);
+    if (profile_system_server) {
+      jit_options_->SetWaitForJitNotificationsToSaveProfile(false);
+      VLOG(profiler) << "Enabling system server profiles";
+    }
+  }
+  if (!safe_mode_ &&
       (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) &&
       jit_ == nullptr) {
     // Note that when running ART standalone (not zygote, nor zygote fork),
diff --git a/runtime/runtime.h b/runtime/runtime.h
index c8aa4e5..c0ff6b3 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -446,7 +446,11 @@
 
   void PreZygoteFork();
   void InitNonZygoteOrPostFork(
-      JNIEnv* env, bool is_system_server, NativeBridgeAction action, const char* isa);
+      JNIEnv* env,
+      bool is_system_server,
+      NativeBridgeAction action,
+      const char* isa,
+      bool profile_system_server = false);
 
   const instrumentation::Instrumentation* GetInstrumentation() const {
     return &instrumentation_;
diff --git a/tools/veridex/flow_analysis.cc b/tools/veridex/flow_analysis.cc
index e2833bf..154c60f 100644
--- a/tools/veridex/flow_analysis.cc
+++ b/tools/veridex/flow_analysis.cc
@@ -112,7 +112,12 @@
       RegisterValue(RegisterSource::kNone, DexFileReference(nullptr, 0), cls);
 }
 
-const RegisterValue& VeriFlowAnalysis::GetRegister(uint32_t dex_register) {
+void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, int32_t value, const VeriClass* cls) {
+  current_registers_[dex_register] =
+      RegisterValue(RegisterSource::kConstant, value, DexFileReference(nullptr, 0), cls);
+}
+
+const RegisterValue& VeriFlowAnalysis::GetRegister(uint32_t dex_register) const {
   return current_registers_[dex_register];
 }
 
@@ -131,6 +136,49 @@
   return RegisterValue(RegisterSource::kField, DexFileReference(&dex_file, field_index), cls);
 }
 
+int VeriFlowAnalysis::GetBranchFlags(const Instruction& instruction) const {
+  switch (instruction.Opcode()) {
+    #define IF_XX(cond, op) \
+    case Instruction::IF_##cond: { \
+      RegisterValue lhs = GetRegister(instruction.VRegA()); \
+      RegisterValue rhs = GetRegister(instruction.VRegB()); \
+      if (lhs.IsConstant() && rhs.IsConstant()) { \
+        if (lhs.GetConstant() op rhs.GetConstant()) { \
+          return Instruction::kBranch; \
+        } else { \
+          return Instruction::kContinue; \
+        } \
+      } \
+      break; \
+    } \
+    case Instruction::IF_##cond##Z: { \
+      RegisterValue val = GetRegister(instruction.VRegA()); \
+      if (val.IsConstant()) { \
+        if (val.GetConstant() op 0) { \
+          return Instruction::kBranch; \
+        } else { \
+          return Instruction::kContinue; \
+        } \
+      } \
+      break; \
+    }
+
+    IF_XX(EQ, ==);
+    IF_XX(NE, !=);
+    IF_XX(LT, <);
+    IF_XX(LE, <=);
+    IF_XX(GT, >);
+    IF_XX(GE, >=);
+
+    #undef IF_XX
+
+    default:
+      break;
+  }
+
+  return Instruction::FlagsOf(instruction.Opcode());
+}
+
 void VeriFlowAnalysis::AnalyzeCode() {
   std::vector<uint32_t> work_list;
   work_list.push_back(0);
@@ -149,16 +197,17 @@
       ProcessDexInstruction(inst);
       SetVisited(dex_pc);
 
-      int opcode_flags = Instruction::FlagsOf(inst.Opcode());
-      if ((opcode_flags & Instruction::kContinue) != 0) {
-        if ((opcode_flags & Instruction::kBranch) != 0) {
+      int branch_flags = GetBranchFlags(inst);
+
+      if ((branch_flags & Instruction::kContinue) != 0) {
+        if ((branch_flags & Instruction::kBranch) != 0) {
           uint32_t branch_dex_pc = dex_pc + inst.GetTargetOffset();
           if (MergeRegisterValues(branch_dex_pc)) {
             work_list.push_back(branch_dex_pc);
           }
         }
         dex_pc += inst.SizeInCodeUnits();
-      } else if ((opcode_flags & Instruction::kBranch) != 0) {
+      } else if ((branch_flags & Instruction::kBranch) != 0) {
         dex_pc += inst.GetTargetOffset();
         DCHECK(IsBranchTarget(dex_pc));
       } else {
@@ -178,12 +227,30 @@
 
 void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) {
   switch (instruction.Opcode()) {
-    case Instruction::CONST_4:
-    case Instruction::CONST_16:
-    case Instruction::CONST:
+    case Instruction::CONST_4: {
+      int32_t register_index = instruction.VRegA();
+      int32_t value = instruction.VRegB_11n();
+      UpdateRegister(register_index, value, VeriClass::integer_);
+      break;
+    }
+    case Instruction::CONST_16: {
+      int32_t register_index = instruction.VRegA();
+      int32_t value = instruction.VRegB_21s();
+      UpdateRegister(register_index, value, VeriClass::integer_);
+      break;
+    }
+
+    case Instruction::CONST: {
+      int32_t register_index = instruction.VRegA();
+      int32_t value = instruction.VRegB_31i();
+      UpdateRegister(register_index, value, VeriClass::integer_);
+      break;
+    }
+
     case Instruction::CONST_HIGH16: {
       int32_t register_index = instruction.VRegA();
-      UpdateRegister(register_index, VeriClass::integer_);
+      int32_t value = instruction.VRegB_21h();
+      UpdateRegister(register_index, value, VeriClass::integer_);
       break;
     }
 
@@ -268,6 +335,8 @@
     case Instruction::RETURN: {
       break;
     }
+
+    // If operations will be handled when looking at the control flow.
     #define IF_XX(cond) \
     case Instruction::IF_##cond: break; \
     case Instruction::IF_##cond##Z: break
@@ -279,6 +348,8 @@
     IF_XX(GT);
     IF_XX(GE);
 
+    #undef IF_XX
+
     case Instruction::GOTO:
     case Instruction::GOTO_16:
     case Instruction::GOTO_32: {
@@ -495,7 +566,13 @@
     case Instruction::SGET_BYTE:
     case Instruction::SGET_CHAR:
     case Instruction::SGET_SHORT: {
-      UpdateRegister(instruction.VRegA_22c(), GetFieldType(instruction.VRegC_22c()));
+      uint32_t dest_reg = instruction.VRegA_21c();
+      uint16_t field_index = instruction.VRegB_21c();
+      if (VeriClass::sdkInt_ != nullptr && resolver_->GetField(field_index) == VeriClass::sdkInt_) {
+        UpdateRegister(dest_reg, gTargetSdkVersion, VeriClass::integer_);
+      } else {
+        UpdateRegister(dest_reg, GetFieldType(instruction.VRegC_22c()));
+      }
       break;
     }
 
diff --git a/tools/veridex/flow_analysis.h b/tools/veridex/flow_analysis.h
index 62c9916..fc09360 100644
--- a/tools/veridex/flow_analysis.h
+++ b/tools/veridex/flow_analysis.h
@@ -35,6 +35,7 @@
   kMethod,
   kClass,
   kString,
+  kConstant,
   kNone
 };
 
@@ -44,28 +45,33 @@
 class RegisterValue {
  public:
   RegisterValue() : source_(RegisterSource::kNone),
-                    parameter_index_(0),
+                    value_(0),
                     reference_(nullptr, 0),
                     type_(nullptr) {}
   RegisterValue(RegisterSource source, DexFileReference reference, const VeriClass* type)
-      : source_(source), parameter_index_(0), reference_(reference), type_(type) {}
+      : source_(source), value_(0), reference_(reference), type_(type) {}
 
   RegisterValue(RegisterSource source,
-                uint32_t parameter_index,
+                uint32_t value,
                 DexFileReference reference,
                 const VeriClass* type)
-      : source_(source), parameter_index_(parameter_index), reference_(reference), type_(type) {}
+      : source_(source), value_(value), reference_(reference), type_(type) {}
 
   RegisterSource GetSource() const { return source_; }
   DexFileReference GetDexFileReference() const { return reference_; }
   const VeriClass* GetType() const { return type_; }
   uint32_t GetParameterIndex() const {
     CHECK(IsParameter());
-    return parameter_index_;
+    return value_;
+  }
+  uint32_t GetConstant() const {
+    CHECK(IsConstant());
+    return value_;
   }
   bool IsParameter() const { return source_ == RegisterSource::kParameter; }
   bool IsClass() const { return source_ == RegisterSource::kClass; }
   bool IsString() const { return source_ == RegisterSource::kString; }
+  bool IsConstant() const { return source_ == RegisterSource::kConstant; }
 
   std::string ToString() const {
     switch (source_) {
@@ -91,7 +97,7 @@
 
  private:
   RegisterSource source_;
-  uint32_t parameter_index_;
+  uint32_t value_;
   DexFileReference reference_;
   const VeriClass* type_;
 };
@@ -137,12 +143,15 @@
       uint32_t dex_register, RegisterSource kind, VeriClass* cls, uint32_t source_id);
   void UpdateRegister(uint32_t dex_register, const RegisterValue& value);
   void UpdateRegister(uint32_t dex_register, const VeriClass* cls);
+  void UpdateRegister(uint32_t dex_register, int32_t value, const VeriClass* cls);
   void ProcessDexInstruction(const Instruction& inst);
   void SetVisited(uint32_t dex_pc);
   RegisterValue GetFieldType(uint32_t field_index);
 
+  int GetBranchFlags(const Instruction& instruction) const;
+
  protected:
-  const RegisterValue& GetRegister(uint32_t dex_register);
+  const RegisterValue& GetRegister(uint32_t dex_register) const;
   RegisterValue GetReturnType(uint32_t method_index);
 
   VeridexResolver* resolver_;
diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc
index dc7ea94..bcd4815 100644
--- a/tools/veridex/veridex.cc
+++ b/tools/veridex/veridex.cc
@@ -25,6 +25,7 @@
 #include "precise_hidden_api_finder.h"
 #include "resolver.h"
 
+#include <cstdlib>
 #include <sstream>
 
 namespace art {
@@ -62,6 +63,7 @@
 VeriMethod VeriClass::getDeclaredMethod_ = nullptr;
 VeriMethod VeriClass::getClass_ = nullptr;
 VeriMethod VeriClass::loadClass_ = nullptr;
+VeriField VeriClass::sdkInt_ = nullptr;
 
 struct VeridexOptions {
   const char* dex_file = nullptr;
@@ -70,6 +72,7 @@
   const char* light_greylist = nullptr;
   const char* dark_greylist = nullptr;
   bool precise = true;
+  int target_sdk_version = 28; /* P */
 };
 
 static const char* Substr(const char* str, int index) {
@@ -91,6 +94,7 @@
   static const char* kDarkGreylistOption = "--dark-greylist=";
   static const char* kLightGreylistOption = "--light-greylist=";
   static const char* kImprecise = "--imprecise";
+  static const char* kTargetSdkVersion = "--target-sdk-version=";
 
   for (int i = 0; i < argc; ++i) {
     if (StartsWith(argv[i], kDexFileOption)) {
@@ -105,6 +109,8 @@
       options->light_greylist = Substr(argv[i], strlen(kLightGreylistOption));
     } else if (strcmp(argv[i], kImprecise) == 0) {
       options->precise = false;
+    } else if (StartsWith(argv[i], kTargetSdkVersion)) {
+      options->target_sdk_version = atoi(Substr(argv[i], strlen(kTargetSdkVersion)));
     }
   }
 }
@@ -124,6 +130,7 @@
   static int Run(int argc, char** argv) {
     VeridexOptions options;
     ParseArgs(&options, argc, argv);
+    gTargetSdkVersion = options.target_sdk_version;
 
     std::vector<std::string> boot_content;
     std::vector<std::string> app_content;
@@ -200,6 +207,11 @@
     VeriClass::loadClass_ = boot_resolvers[0]->LookupDeclaredMethodIn(
         *VeriClass::class_loader_, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
 
+    VeriClass* version = type_map["Landroid/os/Build$VERSION;"];
+    if (version != nullptr) {
+      VeriClass::sdkInt_ = boot_resolvers[0]->LookupFieldIn(*version, "SDK_INT", "I");
+    }
+
     std::vector<std::unique_ptr<VeridexResolver>> app_resolvers;
     Resolve(app_dex_files, resolver_map, type_map, &app_resolvers);
 
diff --git a/tools/veridex/veridex.h b/tools/veridex/veridex.h
index 9c0a158..31ddbf4 100644
--- a/tools/veridex/veridex.h
+++ b/tools/veridex/veridex.h
@@ -24,6 +24,8 @@
 
 namespace art {
 
+static int gTargetSdkVersion = 1000;  // Will be initialized after parsing options.
+
 /**
  * Abstraction for fields defined in dex files. Currently, that's a pointer into their
  * `encoded_field` description.
@@ -86,6 +88,8 @@
   static VeriMethod getClass_;
   static VeriMethod loadClass_;
 
+  static VeriField sdkInt_;
+
  private:
   Primitive::Type kind_;
   uint8_t dimensions_;