Merge "Refactor JVMTI ClassFileLoadHook handling"
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 1e79fdf..3cb9731 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -369,7 +369,7 @@
  */
 TEST_F(CmdlineParserTest, TestJdwpProviderEmpty) {
   {
-    EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kInternal, "", M::JdwpProvider);
+    EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kNone, "", M::JdwpProvider);
   }
 }  // TEST_F
 
diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc
index 6c66f12..a8d2a37 100644
--- a/openjdkjvmti/fixed_up_dex_file.cc
+++ b/openjdkjvmti/fixed_up_dex_file.cc
@@ -33,6 +33,7 @@
 #include "dex/art_dex_file_loader.h"
 #include "dex/dex_file-inl.h"
 #include "dex/dex_file_loader.h"
+#include "dex/dex_file_verifier.h"
 
 // Runtime includes.
 #include "dex_container.h"
@@ -67,6 +68,20 @@
   vdex->UnquickenDexFile(new_dex_file, original_dex_file, /* decompile_return_instruction */true);
 }
 
+static void DCheckVerifyDexFile(const art::DexFile& dex) {
+  if (art::kIsDebugBuild) {
+    std::string error;
+    if (!art::DexFileVerifier::Verify(&dex,
+                                      dex.Begin(),
+                                      dex.Size(),
+                                      "FixedUpDexFile_Verification.dex",
+                                      /*verify_checksum*/ true,
+                                      &error)) {
+      LOG(FATAL) << "Failed to verify de-quickened dex file: " << error;
+    }
+  }
+}
+
 std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& original,
                                                        const char* descriptor) {
   // Copy the data into mutable memory.
@@ -121,6 +136,7 @@
   DoDexUnquicken(*new_dex_file, original);
 
   RecomputeDexChecksum(const_cast<art::DexFile*>(new_dex_file.get()));
+  DCheckVerifyDexFile(*new_dex_file);
   std::unique_ptr<FixedUpDexFile> ret(new FixedUpDexFile(std::move(new_dex_file), std::move(data)));
   return ret;
 }
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index 642d26e..c75f3e9 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -209,7 +209,8 @@
 
   bool CreateProfile(const std::string& profile_file_contents,
                      const std::string& filename,
-                     const std::string& dex_location) {
+                     const std::string& dex_location,
+                     bool skip_verification) {
     ScratchFile class_names_file;
     File* file = class_names_file.GetFile();
     EXPECT_TRUE(file->WriteFully(profile_file_contents.c_str(), profile_file_contents.length()));
@@ -222,6 +223,9 @@
     argv_str.push_back("--reference-profile-file=" + filename);
     argv_str.push_back("--apk=" + dex_location);
     argv_str.push_back("--dex-location=" + dex_location);
+    if (skip_verification) {
+      argv_str.push_back("--skip-apk-verification");
+    }
     std::string error;
     EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0);
     return true;
@@ -238,6 +242,7 @@
     argv_str.push_back("--profile-file=" + filename);
     argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]);
     argv_str.push_back("--dex-location=" + GetLibCoreDexFileNames()[0]);
+    argv_str.push_back("--skip-apk-verification");
     argv_str.push_back("--dump-output-to-fd=" + std::to_string(GetFd(output_file)));
     std::string error;
     EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0);
@@ -268,7 +273,8 @@
     ScratchFile profile_file;
     EXPECT_TRUE(CreateProfile(input_file_contents,
                               profile_file.GetFilename(),
-                              GetLibCoreDexFileNames()[0]));
+                              GetLibCoreDexFileNames()[0],
+                              /* skip_verification */ true));
     profile_file.GetFile()->ResetOffset();
     EXPECT_TRUE(DumpClassesAndMethods(profile_file.GetFilename(), output_file_contents));
     return true;
@@ -675,7 +681,8 @@
   ScratchFile profile_file;
   EXPECT_TRUE(CreateProfile(input_file_contents,
                             profile_file.GetFilename(),
-                            GetLibCoreDexFileNames()[0]));
+                            GetLibCoreDexFileNames()[0],
+                            /* skip_verification */ true));
   ProfileCompilationInfo info;
   profile_file.GetFile()->ResetOffset();
   ASSERT_TRUE(info.Load(GetFd(profile_file)));
@@ -731,7 +738,8 @@
       "H" + kHotMethod + "\n" +
       kUncommonDirtyClass;
   profiles.emplace_back(ScratchFile());
-  EXPECT_TRUE(CreateProfile(dex1, profiles.back().GetFilename(), core_dex));
+  EXPECT_TRUE(CreateProfile(
+      dex1, profiles.back().GetFilename(), core_dex, /* skip_verification */ true));
 
   // Create a bunch of boot profiles.
   std::string dex2 =
@@ -741,7 +749,8 @@
       "P" + kMultiMethod + "\n" +
       kUncommonDirtyClass;
   profiles.emplace_back(ScratchFile());
-  EXPECT_TRUE(CreateProfile(dex2, profiles.back().GetFilename(), core_dex));
+  EXPECT_TRUE(CreateProfile(
+      dex2, profiles.back().GetFilename(), core_dex, /* skip_verification */ true));
 
   // Create a bunch of boot profiles.
   std::string dex3 =
@@ -750,7 +759,8 @@
       "P" + kMultiMethod + "\n" +
       kDirtyClass + "\n";
   profiles.emplace_back(ScratchFile());
-  EXPECT_TRUE(CreateProfile(dex3, profiles.back().GetFilename(), core_dex));
+  EXPECT_TRUE(CreateProfile(
+      dex3, profiles.back().GetFilename(), core_dex, /* skip_verification */ true));
 
   // Generate the boot profile.
   ScratchFile out_profile;
@@ -763,6 +773,7 @@
   args.push_back("--reference-profile-file=" + out_profile.GetFilename());
   args.push_back("--apk=" + core_dex);
   args.push_back("--dex-location=" + core_dex);
+  args.push_back("--skip-apk-verification");
   for (const ScratchFile& profile : profiles) {
     args.push_back("--profile-file=" + profile.GetFilename());
   }
@@ -858,7 +869,8 @@
   ScratchFile profile_file;
   ASSERT_TRUE(CreateProfile(input_file_contents,
                             profile_file.GetFilename(),
-                            GetTestDexFileName("ProfileTestMultiDex")));
+                            GetTestDexFileName("ProfileTestMultiDex"),
+                            /* skip_verification */ false));
 
   // Load the profile from disk.
   ProfileCompilationInfo info;
@@ -1008,7 +1020,8 @@
   std::string dex_filename = GetTestDexFileName("ProfileTestMultiDex");
   ASSERT_TRUE(CreateProfile(input_file_contents,
                             profile_file.GetFilename(),
-                            dex_filename));
+                            dex_filename,
+                            /* skip_verification */ false));
 
   // Load the profile from disk.
   ProfileCompilationInfo info;
diff --git a/profman/profman.cc b/profman/profman.cc
index ffc3c01..ea6c382 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -137,6 +137,7 @@
   UsageError("  --apk-fd=<number>: file descriptor containing an open APK to");
   UsageError("      search for dex files");
   UsageError("  --apk-=<filename>: an APK to search for dex files");
+  UsageError("  --skip-apk-verification: do not attempt to verify APKs");
   UsageError("");
   UsageError("  --generate-boot-image-profile: Generate a boot image profile based on input");
   UsageError("      profiles. Requires passing in dex files to inspect properties of classes.");
@@ -185,6 +186,7 @@
       dump_only_(false),
       dump_classes_and_methods_(false),
       generate_boot_image_profile_(false),
+      skip_apk_verification_(false),
       dump_output_to_fd_(kInvalidFd),
       test_profile_num_dex_(kDefaultTestProfileNumDex),
       test_profile_method_percerntage_(kDefaultTestProfileMethodPercentage),
@@ -227,6 +229,8 @@
         ParseUintOption(option, "--dump-output-to-fd", &dump_output_to_fd_, Usage);
       } else if (option == "--generate-boot-image-profile") {
         generate_boot_image_profile_ = true;
+      } else if (option == "--skip-apk-verification") {
+        skip_apk_verification_ = true;
       } else if (option.starts_with("--boot-image-class-threshold=")) {
         ParseUintOption(option,
                         "--boot-image-class-threshold",
@@ -321,6 +325,10 @@
     return result;
   }
 
+  bool ShouldSkipApkVerification() const {
+    return skip_apk_verification_;
+  }
+
   void OpenApkFilesFromLocations(std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
     bool use_apk_fd_list = !apks_fd_.empty();
     if (use_apk_fd_list) {
@@ -342,7 +350,7 @@
       if (use_apk_fd_list) {
         if (dex_file_loader.OpenZip(apks_fd_[i],
                                     dex_locations_[i],
-                                    /* verify */ true,
+                                    /* verify */ !ShouldSkipApkVerification(),
                                     kVerifyChecksum,
                                     &error_msg,
                                     &dex_files_for_location)) {
@@ -353,7 +361,7 @@
       } else {
         if (dex_file_loader.Open(apk_files_[i].c_str(),
                                  dex_locations_[i],
-                                 /* verify */ true,
+                                 /* verify */ !ShouldSkipApkVerification(),
                                  kVerifyChecksum,
                                  &error_msg,
                                  &dex_files_for_location)) {
@@ -1148,6 +1156,7 @@
   bool dump_only_;
   bool dump_classes_and_methods_;
   bool generate_boot_image_profile_;
+  bool skip_apk_verification_;
   int dump_output_to_fd_;
   BootImageOptions boot_image_options_;
   std::string test_profile_;
diff --git a/runtime/dex/dex_file_loader.cc b/runtime/dex/dex_file_loader.cc
index 7dde0a4..0f2758e 100644
--- a/runtime/dex/dex_file_loader.cc
+++ b/runtime/dex/dex_file_loader.cc
@@ -222,8 +222,8 @@
                                                    std::string* error_msg) const {
   return OpenCommon(base,
                     size,
-                    /*data_base*/ base,
-                    /*data_size*/ size,
+                    /*data_base*/ nullptr,
+                    /*data_size*/ 0,
                     location,
                     location_checksum,
                     oat_dex_file,
diff --git a/runtime/leb128.h b/runtime/leb128.h
index 9fb09d8..07eadc1 100644
--- a/runtime/leb128.h
+++ b/runtime/leb128.h
@@ -55,6 +55,10 @@
   return static_cast<uint32_t>(result);
 }
 
+static inline uint32_t DecodeUnsignedLeb128WithoutMovingCursor(const uint8_t* data) {
+  return DecodeUnsignedLeb128(&data);
+}
+
 static inline bool DecodeUnsignedLeb128Checked(const uint8_t** data,
                                                const void* end,
                                                uint32_t* out) {
@@ -203,6 +207,34 @@
   return (x * 37) >> 8;
 }
 
+static inline bool IsLeb128Terminator(const uint8_t* ptr) {
+  return *ptr <= 0x7f;
+}
+
+// Returns the first byte of a Leb128 value assuming that:
+// (1) `end_ptr` points to the first byte after the Leb128 value, and
+// (2) there is another Leb128 value before this one.
+template <typename T>
+static inline T* ReverseSearchUnsignedLeb128(T* end_ptr) {
+  static_assert(std::is_same<typename std::remove_const<T>::type, uint8_t>::value,
+                "T must be a uint8_t");
+  T* ptr = end_ptr;
+
+  // Move one byte back, check that this is the terminating byte.
+  ptr--;
+  DCHECK(IsLeb128Terminator(ptr));
+
+  // Keep moving back while the previous byte is not a terminating byte.
+  // Fail after reading five bytes in case there isn't another Leb128 value
+  // before this one.
+  while (!IsLeb128Terminator(ptr - 1)) {
+    ptr--;
+    DCHECK_LE(static_cast<ptrdiff_t>(end_ptr - ptr), 5);
+  }
+
+  return ptr;
+}
+
 // Returns the number of bytes needed to encode the value in unsigned LEB128.
 static inline uint32_t SignedLeb128Size(int32_t data) {
   // Like UnsignedLeb128Size(), but we need one bit beyond the highest bit that differs from sign.
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 6d065d6..b1286da 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1253,7 +1253,20 @@
   jdwp_provider_ = runtime_options.GetOrDefault(Opt::JdwpProvider);
   switch (jdwp_provider_) {
     case JdwpProvider::kNone: {
-      LOG(WARNING) << "Disabling all JDWP support.";
+      LOG(INFO) << "Disabling all JDWP support.";
+      if (!jdwp_options_.empty()) {
+        bool has_transport = jdwp_options_.find("transport") != std::string::npos;
+        const char* transport_internal = !has_transport ? "transport=dt_android_adb," : "";
+        std::string adb_connection_args =
+            std::string("  -XjdwpProvider:adbconnection -XjdwpOptions:") + jdwp_options_;
+        LOG(WARNING) << "Jdwp options given when jdwp is disabled! You probably want to enable "
+                     << "jdwp with one of:" << std::endl
+                     << "  -XjdwpProvider:internal "
+                     << "-XjdwpOptions:" << transport_internal << jdwp_options_ << std::endl
+                     << "  -Xplugin:libopenjdkjvmti" << (kIsDebugBuild ? "d" : "") << ".so "
+                     << "-agentpath:libjdwp.so=" << jdwp_options_ << std::endl
+                     << (has_transport ? "" : adb_connection_args);
+      }
       break;
     }
     case JdwpProvider::kInternal: {
@@ -1855,7 +1868,13 @@
 bool Runtime::AttachCurrentThread(const char* thread_name, bool as_daemon, jobject thread_group,
                                   bool create_peer) {
   ScopedTrace trace(__FUNCTION__);
-  return Thread::Attach(thread_name, as_daemon, thread_group, create_peer) != nullptr;
+  Thread* self = Thread::Attach(thread_name, as_daemon, thread_group, create_peer);
+  // Run ThreadGroup.add to notify the group that this thread is now started.
+  if (self != nullptr && create_peer && !IsAotCompiler()) {
+    ScopedObjectAccess soa(self);
+    self->NotifyThreadGroup(soa, thread_group);
+  }
+  return self != nullptr;
 }
 
 void Runtime::DetachCurrentThread() {
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 6e1a68b..e78d952 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -44,7 +44,7 @@
 RUNTIME_OPTIONS_KEY (Unit,                CheckJni)
 RUNTIME_OPTIONS_KEY (Unit,                JniOptsForceCopy)
 RUNTIME_OPTIONS_KEY (std::string,         JdwpOptions, "")
-RUNTIME_OPTIONS_KEY (JdwpProvider,        JdwpProvider, JdwpProvider::kInternal)
+RUNTIME_OPTIONS_KEY (JdwpProvider,        JdwpProvider,                   JdwpProvider::kNone)
 RUNTIME_OPTIONS_KEY (MemoryKiB,           MemoryMaximumSize,              gc::Heap::kDefaultMaximumSize)  // -Xmx
 RUNTIME_OPTIONS_KEY (MemoryKiB,           MemoryInitialSize,              gc::Heap::kDefaultInitialSize)  // -Xms
 RUNTIME_OPTIONS_KEY (MemoryKiB,           HeapGrowthLimit)                // Default is 0 for unlimited
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 46cb751..14375f7 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2049,20 +2049,8 @@
 
   // The thread counts as started from now on. We need to add it to the ThreadGroup. For regular
   // threads, this is done in Thread.start() on the Java side.
-  {
-    // This is only ever done once. There's no benefit in caching the method.
-    jmethodID thread_group_add = soa.Env()->GetMethodID(WellKnownClasses::java_lang_ThreadGroup,
-                                                        "add",
-                                                        "(Ljava/lang/Thread;)V");
-    CHECK(thread_group_add != nullptr);
-    ScopedLocalRef<jobject> thread_jobject(
-        soa.Env(), soa.Env()->AddLocalReference<jobject>(Thread::Current()->GetPeer()));
-    soa.Env()->CallNonvirtualVoidMethod(runtime->GetMainThreadGroup(),
-                                        WellKnownClasses::java_lang_ThreadGroup,
-                                        thread_group_add,
-                                        thread_jobject.get());
-    Thread::Current()->AssertNoPendingException();
-  }
+  Thread::Current()->NotifyThreadGroup(soa, runtime->GetMainThreadGroup());
+  Thread::Current()->AssertNoPendingException();
 }
 
 void Thread::Shutdown() {
@@ -2076,6 +2064,28 @@
   }
 }
 
+void Thread::NotifyThreadGroup(ScopedObjectAccessAlreadyRunnable& soa, jobject thread_group) {
+  ScopedLocalRef<jobject> thread_jobject(
+      soa.Env(), soa.Env()->AddLocalReference<jobject>(Thread::Current()->GetPeer()));
+  ScopedLocalRef<jobject> thread_group_jobject_scoped(
+      soa.Env(), nullptr);
+  jobject thread_group_jobject = thread_group;
+  if (thread_group == nullptr || kIsDebugBuild) {
+    // There is always a group set. Retrieve it.
+    thread_group_jobject_scoped.reset(
+        soa.Env()->GetObjectField(thread_jobject.get(),
+                                  WellKnownClasses::java_lang_Thread_group));
+    thread_group_jobject = thread_group_jobject_scoped.get();
+    if (kIsDebugBuild && thread_group != nullptr) {
+      CHECK(soa.Env()->IsSameObject(thread_group, thread_group_jobject));
+    }
+  }
+  soa.Env()->CallNonvirtualVoidMethod(thread_group_jobject,
+                                      WellKnownClasses::java_lang_ThreadGroup,
+                                      WellKnownClasses::java_lang_ThreadGroup_add,
+                                      thread_jobject.get());
+}
+
 Thread::Thread(bool daemon)
     : tls32_(daemon),
       wait_monitor_(nullptr),
diff --git a/runtime/thread.h b/runtime/thread.h
index 426d27d..295685e 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -511,6 +511,12 @@
   static void FinishStartup();
   static void Shutdown();
 
+  // Notify this thread's thread-group that this thread has started.
+  // Note: the given thread-group is used as a fast path and verified in debug build. If the value
+  //       is null, the thread's thread-group is loaded from the peer.
+  void NotifyThreadGroup(ScopedObjectAccessAlreadyRunnable& soa, jobject thread_group = nullptr)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   // JNI methods
   JNIEnvExt* GetJniEnv() const {
     return tlsPtr_.jni_env;
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index 36ebb17..7428e98 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -30,6 +30,8 @@
 #include "dex/dex_file.h"
 #include "dex/dex_file_loader.h"
 #include "dex_to_dex_decompiler.h"
+#include "hidden_api_access_flags.h"
+#include "leb128.h"
 #include "quicken_info.h"
 
 namespace art {
@@ -262,6 +264,18 @@
   UnquickenDexFile(target_dex_file, source_dex_file.Begin(), decompile_return_instruction);
 }
 
+static void UpdateAccessFlags(uint8_t* data, uint32_t new_flag, bool is_method) {
+  // Go back 1 uleb to start.
+  data = ReverseSearchUnsignedLeb128(data);
+  if (is_method) {
+    // Methods have another uleb field before the access flags
+    data = ReverseSearchUnsignedLeb128(data);
+  }
+  DCHECK_EQ(HiddenApiAccessFlags::RemoveFromDex(DecodeUnsignedLeb128WithoutMovingCursor(data)),
+            new_flag);
+  UpdateUnsignedLeb128(data, new_flag);
+}
+
 void VdexFile::UnquickenDexFile(const DexFile& target_dex_file,
                                 const uint8_t* source_dex_begin,
                                 bool decompile_return_instruction) const {
@@ -280,27 +294,32 @@
       for (ClassDataItemIterator class_it(target_dex_file, class_data);
            class_it.HasNext();
            class_it.Next()) {
-        if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) {
+        if (class_it.IsAtMethod()) {
           const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem();
-          if (!unquickened_code_item.emplace(code_item).second) {
-            // Already unquickened this code item, do not do it again.
-            continue;
+          if (code_item != nullptr && unquickened_code_item.emplace(code_item).second) {
+            ArrayRef<const uint8_t> quicken_data;
+            if (!quickening_info.empty()) {
+              const uint32_t quickening_offset = GetQuickeningInfoOffset(
+                  GetQuickenInfoOffsetTable(source_dex_begin,
+                                            target_dex_file.NumMethodIds(),
+                                            quickening_info),
+                  class_it.GetMemberIndex(),
+                  quickening_info);
+              quicken_data = GetQuickeningInfoAt(quickening_info, quickening_offset);
+            }
+            optimizer::ArtDecompileDEX(
+                target_dex_file,
+                *code_item,
+                quicken_data,
+                decompile_return_instruction);
           }
-          ArrayRef<const uint8_t> quicken_data;
-          if (!quickening_info.empty()) {
-            const uint32_t quickening_offset = GetQuickeningInfoOffset(
-                GetQuickenInfoOffsetTable(source_dex_begin,
-                                          target_dex_file.NumMethodIds(),
-                                          quickening_info),
-                class_it.GetMemberIndex(),
-                quickening_info);
-            quicken_data = GetQuickeningInfoAt(quickening_info, quickening_offset);
-          }
-          optimizer::ArtDecompileDEX(
-              target_dex_file,
-              *code_item,
-              quicken_data,
-              decompile_return_instruction);
+          UpdateAccessFlags(const_cast<uint8_t*>(class_it.DataPointer()),
+                            class_it.GetMemberAccessFlags(),
+                            /*is_method*/ true);
+        } else {
+          UpdateAccessFlags(const_cast<uint8_t*>(class_it.DataPointer()),
+                            class_it.GetMemberAccessFlags(),
+                            /*is_method*/ false);
         }
       }
     }
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index dc57f81..5fe10f5 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -110,6 +110,7 @@
 jmethodID WellKnownClasses::java_lang_Thread_dispatchUncaughtException;
 jmethodID WellKnownClasses::java_lang_Thread_init;
 jmethodID WellKnownClasses::java_lang_Thread_run;
+jmethodID WellKnownClasses::java_lang_ThreadGroup_add;
 jmethodID WellKnownClasses::java_lang_ThreadGroup_removeThread;
 jmethodID WellKnownClasses::java_nio_DirectByteBuffer_init;
 jmethodID WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation;
@@ -347,6 +348,7 @@
   java_lang_Thread_dispatchUncaughtException = CacheMethod(env, java_lang_Thread, false, "dispatchUncaughtException", "(Ljava/lang/Throwable;)V");
   java_lang_Thread_init = CacheMethod(env, java_lang_Thread, false, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
   java_lang_Thread_run = CacheMethod(env, java_lang_Thread, false, "run", "()V");
+  java_lang_ThreadGroup_add = CacheMethod(env, java_lang_ThreadGroup, false, "add", "(Ljava/lang/Thread;)V");
   java_lang_ThreadGroup_removeThread = CacheMethod(env, java_lang_ThreadGroup, false, "threadTerminated", "(Ljava/lang/Thread;)V");
   java_nio_DirectByteBuffer_init = CacheMethod(env, java_nio_DirectByteBuffer, false, "<init>", "(JI)V");
   libcore_reflect_AnnotationFactory_createAnnotation = CacheMethod(env, libcore_reflect_AnnotationFactory, true, "createAnnotation", "(Ljava/lang/Class;[Llibcore/reflect/AnnotationMember;)Ljava/lang/annotation/Annotation;");
@@ -496,6 +498,7 @@
   java_lang_Thread_dispatchUncaughtException = nullptr;
   java_lang_Thread_init = nullptr;
   java_lang_Thread_run = nullptr;
+  java_lang_ThreadGroup_add = nullptr;
   java_lang_ThreadGroup_removeThread = nullptr;
   java_nio_DirectByteBuffer_init = nullptr;
   libcore_reflect_AnnotationFactory_createAnnotation = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 024971a..9e0b079 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -121,6 +121,7 @@
   static jmethodID java_lang_Thread_dispatchUncaughtException;
   static jmethodID java_lang_Thread_init;
   static jmethodID java_lang_Thread_run;
+  static jmethodID java_lang_ThreadGroup_add;
   static jmethodID java_lang_ThreadGroup_removeThread;
   static jmethodID java_nio_DirectByteBuffer_init;
   static jmethodID libcore_reflect_AnnotationFactory_createAnnotation;
diff --git a/test/004-ThreadStress/src/Main.java b/test/004-ThreadStress/src/Main.java
index 6ad160c..c03a912 100644
--- a/test/004-ThreadStress/src/Main.java
+++ b/test/004-ThreadStress/src/Main.java
@@ -38,6 +38,8 @@
 //    -t X ............ number of operations per thread
 //    -p X ............ number of permits granted by semaphore
 //    --dumpmap ....... print the frequency map
+//    --locks-only .... select a pre-set frequency map with lock-related operations only
+//    --allocs-only ... select a pre-set frequency map with allocation-related operations only
 //    -oom:X .......... frequency of OOM (double)
 //    -sigquit:X ...... frequency of SigQuit (double)
 //    -alloc:X ........ frequency of Alloc (double)
@@ -289,26 +291,35 @@
     private final static Map<Operation, Double> createDefaultFrequencyMap(Object lock,
             Semaphore semaphore) {
         Map<Operation, Double> frequencyMap = new HashMap<Operation, Double>();
-        frequencyMap.put(new OOM(), 0.005);                   //  1/200
-        frequencyMap.put(new SigQuit(), 0.095);               // 19/200
-        frequencyMap.put(new Alloc(), 0.225);                 // 45/200
-        frequencyMap.put(new LargeAlloc(), 0.05);             // 10/200
-        frequencyMap.put(new StackTrace(), 0.1);              // 20/200
-        frequencyMap.put(new Exit(), 0.225);                  // 45/200
-        frequencyMap.put(new Sleep(), 0.125);                 // 25/200
-        frequencyMap.put(new TimedWait(lock), 0.05);          // 10/200
-        frequencyMap.put(new Wait(lock), 0.075);              // 15/200
-        frequencyMap.put(new QueuedWait(semaphore), 0.05);    // 10/200
+        frequencyMap.put(new OOM(), 0.005);                   //   1/200
+        frequencyMap.put(new SigQuit(), 0.095);               //  19/200
+        frequencyMap.put(new Alloc(), 0.225);                 //  45/200
+        frequencyMap.put(new LargeAlloc(), 0.05);             //  10/200
+        frequencyMap.put(new StackTrace(), 0.1);              //  20/200
+        frequencyMap.put(new Exit(), 0.225);                  //  45/200
+        frequencyMap.put(new Sleep(), 0.125);                 //  25/200
+        frequencyMap.put(new TimedWait(lock), 0.05);          //  10/200
+        frequencyMap.put(new Wait(lock), 0.075);              //  15/200
+        frequencyMap.put(new QueuedWait(semaphore), 0.05);    //  10/200
+
+        return frequencyMap;
+    }
+
+    private final static Map<Operation, Double> createAllocFrequencyMap() {
+        Map<Operation, Double> frequencyMap = new HashMap<Operation, Double>();
+        frequencyMap.put(new Sleep(), 0.2);                   //  40/200
+        frequencyMap.put(new Alloc(), 0.65);                  // 130/200
+        frequencyMap.put(new LargeAlloc(), 0.15);             //  30/200
 
         return frequencyMap;
     }
 
     private final static Map<Operation, Double> createLockFrequencyMap(Object lock) {
       Map<Operation, Double> frequencyMap = new HashMap<Operation, Double>();
-      frequencyMap.put(new Sleep(), 0.2);                     // 40/200
-      frequencyMap.put(new TimedWait(lock), 0.2);             // 40/200
-      frequencyMap.put(new Wait(lock), 0.2);                  // 40/200
-      frequencyMap.put(new SyncAndWork(lock), 0.4);           // 80/200
+      frequencyMap.put(new Sleep(), 0.2);                     //  40/200
+      frequencyMap.put(new TimedWait(lock), 0.2);             //  40/200
+      frequencyMap.put(new Wait(lock), 0.2);                  //  40/200
+      frequencyMap.put(new SyncAndWork(lock), 0.4);           //  80/200
 
       return frequencyMap;
     }
@@ -414,11 +425,14 @@
                     i++;
                     permits = Integer.parseInt(args[i]);
                 } else if (args[i].equals("--locks-only")) {
-                    lock = new Object();
                     frequencyMap = createLockFrequencyMap(lock);
+                } else if (args[i].equals("--allocs-only")) {
+                    frequencyMap = createAllocFrequencyMap();
                 } else if (args[i].equals("--dumpmap")) {
                     dumpMap = true;
                 } else {
+                    // Processing an argument of the form "-<operation>:X"
+                    // (where X is a double value).
                     Semaphore semaphore = getSemaphore(permits);
                     frequencyMap = updateFrequencyMap(frequencyMap, lock, semaphore, args[i]);
                 }
diff --git a/test/169-threadgroup-jni/expected.txt b/test/169-threadgroup-jni/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/169-threadgroup-jni/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/169-threadgroup-jni/info.txt b/test/169-threadgroup-jni/info.txt
new file mode 100644
index 0000000..b4c77e2
--- /dev/null
+++ b/test/169-threadgroup-jni/info.txt
@@ -0,0 +1 @@
+Ensure that attached threads are correctly handled in ThreadGroups.
diff --git a/test/169-threadgroup-jni/jni_daemon_thread.cc b/test/169-threadgroup-jni/jni_daemon_thread.cc
new file mode 100644
index 0000000..94902dc
--- /dev/null
+++ b/test/169-threadgroup-jni/jni_daemon_thread.cc
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+#include <nativehelper/scoped_local_ref.h>
+#include <pthread.h>
+
+#include <android-base/logging.h>
+
+namespace art {
+
+static JavaVM* vm = nullptr;
+
+static void* Runner(void* arg) {
+  CHECK(vm != nullptr);
+
+  jobject thread_group = reinterpret_cast<jobject>(arg);
+  JNIEnv* env = nullptr;
+  JavaVMAttachArgs args = { JNI_VERSION_1_6, __FUNCTION__, thread_group };
+  int attach_result = vm->AttachCurrentThread(&env, &args);
+  CHECK_EQ(attach_result, 0);
+
+  {
+    ScopedLocalRef<jclass> klass(env, env->FindClass("Main"));
+    CHECK(klass != nullptr);
+
+    jmethodID id = env->GetStaticMethodID(klass.get(), "runFromNative", "()V");
+    CHECK(id != nullptr);
+
+    env->CallStaticVoidMethod(klass.get(), id);
+  }
+
+  int detach_result = vm->DetachCurrentThread();
+  CHECK_EQ(detach_result, 0);
+  return nullptr;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_testNativeThread(
+    JNIEnv* env, jclass, jobject thread_group) {
+  CHECK_EQ(env->GetJavaVM(&vm), 0);
+  jobject global_thread_group = env->NewGlobalRef(thread_group);
+
+  pthread_t pthread;
+  int pthread_create_result = pthread_create(&pthread, nullptr, Runner, global_thread_group);
+  CHECK_EQ(pthread_create_result, 0);
+  int pthread_join_result = pthread_join(pthread, nullptr);
+  CHECK_EQ(pthread_join_result, 0);
+
+  env->DeleteGlobalRef(global_thread_group);
+}
+
+}  // namespace art
diff --git a/test/169-threadgroup-jni/src/Main.java b/test/169-threadgroup-jni/src/Main.java
new file mode 100644
index 0000000..2cd1fcf
--- /dev/null
+++ b/test/169-threadgroup-jni/src/Main.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+    public static void main(String[] args) throws Exception {
+        System.loadLibrary(args[0]);
+
+        ThreadGroup group = new ThreadGroup("Test group");
+        group.setDaemon(true);
+
+        testNativeThread(group);
+
+        if (!executed) {
+          throw new IllegalStateException("Expected runFromNative to be done.");
+        }
+        if (!group.isDestroyed()) {
+            throw new IllegalStateException("Threadgroup should be destroyed.");
+        }
+    }
+
+    private static boolean executed = false;
+    private static void runFromNative() {
+        executed = true;
+    }
+    private static native void testNativeThread(ThreadGroup group);
+}
diff --git a/test/983-source-transform-verify/expected.txt b/test/983-source-transform-verify/expected.txt
index abcdf3a..aa51ea0 100644
--- a/test/983-source-transform-verify/expected.txt
+++ b/test/983-source-transform-verify/expected.txt
@@ -1,2 +1,3 @@
 Dex file hook for art/Test983$Transform
 Dex file hook for java/lang/Object
+Dex file hook for java/lang/ClassLoader
diff --git a/test/983-source-transform-verify/src/art/Test983.java b/test/983-source-transform-verify/src/art/Test983.java
index b81e7f4..faae96a 100644
--- a/test/983-source-transform-verify/src/art/Test983.java
+++ b/test/983-source-transform-verify/src/art/Test983.java
@@ -16,7 +16,6 @@
 
 package art;
 
-import java.util.Base64;
 public class Test983 {
   static class Transform {
     public void sayHi() {
@@ -29,10 +28,11 @@
   }
 
   public static void doTest() {
-    Transform abc = new Transform();
     Redefinition.enableCommonRetransformation(true);
     Redefinition.doCommonClassRetransformation(Transform.class);
     Redefinition.doCommonClassRetransformation(Object.class);
+    // NB java.lang.ClassLoader has hidden fields.
+    Redefinition.doCommonClassRetransformation(ClassLoader.class);
     Redefinition.enableCommonRetransformation(false);
   }
 }
diff --git a/test/Android.bp b/test/Android.bp
index 470a68f..730818c 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -368,6 +368,7 @@
         "149-suspend-all-stress/suspend_all.cc",
         "154-gc-loop/heap_interface.cc",
         "167-visit-locks/visit_locks.cc",
+        "169-threadgroup-jni/jni_daemon_thread.cc",
         "203-multi-checkpoint/multi_checkpoint.cc",
         "305-other-fault-handler/fault_handler.cc",
         "454-get-vreg/get_vreg_jni.cc",
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 5e40b86..ca24471 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -436,7 +436,13 @@
     msg "    adb forward tcp:$PORT tcp:$PORT"
   fi
   msg "    jdb -attach localhost:$PORT"
-  DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
+  if [ "$USE_JVM" = "n" ]; then
+    # TODO We should switch over to using the jvmti agent by default.
+    # Need to tell the runtime to enable the internal jdwp implementation.
+    DEBUGGER_OPTS="-XjdwpOptions:transport=dt_socket,address=$PORT,server=y,suspend=y -XjdwpProvider:internal"
+  else
+    DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
+  fi
 elif [ "$DEBUGGER" = "agent" ]; then
   PORT=12345
   # TODO Support ddms connection and support target.
diff --git a/tools/generate-boot-image-profile.sh b/tools/generate-boot-image-profile.sh
index d87123a..ee53f43 100755
--- a/tools/generate-boot-image-profile.sh
+++ b/tools/generate-boot-image-profile.sh
@@ -46,7 +46,9 @@
   fi
 done
 
-jar_args=()
+# Boot jars have hidden API access flags which do not pass dex file
+# verification. Skip it.
+jar_args=("--skip-apk-verification")
 boot_jars=$("$ANDROID_BUILD_TOP"/art/tools/bootjars.sh --target)
 jar_dir=$ANDROID_BUILD_TOP/$(get_build_var TARGET_OUT_JAVA_LIBRARIES)
 for file in $boot_jars; do
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
index a755fdb..c893da6 100644
--- a/tools/hiddenapi/hiddenapi.cc
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -118,9 +118,11 @@
     // until we hit the terminating byte of the previous Leb128 value.
     const uint8_t* ptr = it_.DataPointer();
     if (it_.IsAtMethod()) {
-      ptr = ReverseSearchUnsignedLeb128(ptr, it_.GetMethodCodeItemOffset());
+      ptr = ReverseSearchUnsignedLeb128(ptr);
+      DCHECK_EQ(DecodeUnsignedLeb128WithoutMovingCursor(ptr), it_.GetMethodCodeItemOffset());
     }
-    ptr = ReverseSearchUnsignedLeb128(ptr, old_flags);
+    ptr = ReverseSearchUnsignedLeb128(ptr);
+    DCHECK_EQ(DecodeUnsignedLeb128WithoutMovingCursor(ptr), old_flags);
 
     // Overwrite the access flags.
     UpdateUnsignedLeb128(const_cast<uint8_t*>(ptr), new_flags);
@@ -158,38 +160,6 @@
     return klass_.GetDexFile().GetFieldId(it_.GetMemberIndex());
   }
 
-  static inline bool IsLeb128Terminator(const uint8_t* ptr) {
-    return *ptr <= 0x7f;
-  }
-
-  // Returns the first byte of a Leb128 value assuming that:
-  // (1) `end_ptr` points to the first byte after the Leb128 value, and
-  // (2) there is another Leb128 value before this one.
-  // The function will fail after reading 5 bytes (the longest supported Leb128
-  // encoding) to protect against situations when (2) is not satisfied.
-  // When a Leb128 value is discovered, it is decoded and CHECKed against `value`.
-  static const uint8_t* ReverseSearchUnsignedLeb128(const uint8_t* end_ptr, uint32_t expected) {
-    const uint8_t* ptr = end_ptr;
-
-    // Move one byte back, check that this is the terminating byte.
-    ptr--;
-    CHECK(IsLeb128Terminator(ptr));
-
-    // Keep moving back while the previous byte is not a terminating byte.
-    // Fail after reading five bytes in case there isn't another Leb128 value
-    // before this one.
-    while (!IsLeb128Terminator(ptr - 1)) {
-      ptr--;
-      CHECK_LE((size_t) (end_ptr - ptr), 5u);
-    }
-
-    // Check that the decoded value matches the `expected` value.
-    const uint8_t* tmp_ptr = ptr;
-    CHECK_EQ(DecodeUnsignedLeb128(&tmp_ptr), expected);
-
-    return ptr;
-  }
-
   const DexClass& klass_;
   const ClassDataItemIterator& it_;
 };
diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py
index bddce32..4a54a3a 100755
--- a/tools/jfuzz/run_jfuzz_test.py
+++ b/tools/jfuzz/run_jfuzz_test.py
@@ -169,7 +169,7 @@
 
   def CompileAndRunTest(self):
     dbg = '-g' if self._debug_info else '-g:none'
-    if RunCommand(['javac', dbg, 'Test.java'],
+    if RunCommand(['javac', '--release=8', dbg, 'Test.java'],
                   out=None, err=None, timeout=30) == RetCode.SUCCESS:
       retc = RunCommand(['java', 'Test'], self.output_file, err=None)
     else: