Merge "Revert "ART: Use the bitstring type check for AOT app compilation.""
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 4f5df03..c8f6615 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -73,6 +73,11 @@
 ART_TEST_HOST_GTEST_MainStripped_DEX := $(basename $(ART_TEST_HOST_GTEST_Main_DEX))Stripped$(suffix $(ART_TEST_HOST_GTEST_Main_DEX))
 ART_TEST_TARGET_GTEST_MainStripped_DEX := $(basename $(ART_TEST_TARGET_GTEST_Main_DEX))Stripped$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX))
 
+# Create rules for MainUncompressed, a copy of Main with the classes.dex uncompressed
+# for the dex2oat tests.
+ART_TEST_HOST_GTEST_MainUncompressed_DEX := $(basename $(ART_TEST_HOST_GTEST_Main_DEX))Uncompressed$(suffix $(ART_TEST_HOST_GTEST_Main_DEX))
+ART_TEST_TARGET_GTEST_MainUncompressed_DEX := $(basename $(ART_TEST_TARGET_GTEST_Main_DEX))Uncompressed$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX))
+
 $(ART_TEST_HOST_GTEST_MainStripped_DEX): $(ART_TEST_HOST_GTEST_Main_DEX)
 	cp $< $@
 	$(call dexpreopt-remove-classes.dex,$@)
@@ -81,6 +86,16 @@
 	cp $< $@
 	$(call dexpreopt-remove-classes.dex,$@)
 
+$(ART_TEST_HOST_GTEST_MainUncompressed_DEX): $(ART_TEST_HOST_GTEST_Main_DEX) $(ZIPALIGN)
+	cp $< $@
+	$(call uncompress-dexs, $@)
+	$(call align-package, $@)
+
+$(ART_TEST_TARGET_GTEST_MainUncompressed_DEX): $(ART_TEST_TARGET_GTEST_Main_DEX) $(ZIPALIGN)
+	cp $< $@
+	$(call uncompress-dexs, $@)
+	$(call align-package, $@)
+
 ART_TEST_GTEST_VerifierDeps_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDeps/*.smali))
 ART_TEST_GTEST_VerifierDepsMulti_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDepsMulti/*.smali))
 ART_TEST_HOST_GTEST_VerifierDeps_DEX := $(dir $(ART_TEST_HOST_GTEST_Main_DEX))$(subst Main,VerifierDeps,$(basename $(notdir $(ART_TEST_HOST_GTEST_Main_DEX))))$(suffix $(ART_TEST_HOST_GTEST_Main_DEX))
@@ -111,7 +126,7 @@
 ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes
 ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested MultiDex
 ART_GTEST_dexlayout_test_DEX_DEPS := ManyMethods
-ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ManyMethods Statics VerifierDeps
+ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ManyMethods Statics VerifierDeps MainUncompressed
 ART_GTEST_dex2oat_image_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps
 ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
 ART_GTEST_hiddenapi_test_DEX_DEPS := HiddenApi
@@ -451,13 +466,27 @@
 
   ART_TEST_HOST_GTEST_DEPENDENCIES += $$(gtest_deps)
 
+.PHONY: $$(gtest_rule)
+ifeq (,$(SANITIZE_HOST))
+$$(gtest_rule): $$(gtest_exe) $$(gtest_deps)
+	$(hide) ($$(call ART_TEST_SKIP,$$@) && $$< && \
+		$$(call ART_TEST_PASSED,$$@)) || $$(call ART_TEST_FAILED,$$@)
+else
 # Note: envsetup currently exports ASAN_OPTIONS=detect_leaks=0 to suppress leak detection, as some
 #       build tools (e.g., ninja) intentionally leak. We want leak checks when we run our tests, so
 #       override ASAN_OPTIONS. b/37751350
-.PHONY: $$(gtest_rule)
+# Note 2: Under sanitization, also capture the output, and run it through the stack tool on failure
+# (with the x86-64 ABI, as this allows symbolization of both x86 and x86-64). We don't do this in
+# general as it loses all the color output, and we have our own symbolization step when not running
+# under ASAN.
 $$(gtest_rule): $$(gtest_exe) $$(gtest_deps)
-	$(hide) ($$(call ART_TEST_SKIP,$$@) && ASAN_OPTIONS=detect_leaks=1 $$< && \
-		$$(call ART_TEST_PASSED,$$@)) || $$(call ART_TEST_FAILED,$$@)
+	$(hide) ($$(call ART_TEST_SKIP,$$@) && set -o pipefail && \
+		ASAN_OPTIONS=detect_leaks=1 $$< 2>&1 | tee $$<.tmp.out >&2 && \
+		{ $$(call ART_TEST_PASSED,$$@) ; rm $$<.tmp.out ; }) || \
+		( grep -q AddressSanitizer $$<.tmp.out && export ANDROID_BUILD_TOP=`pwd` && \
+			{ echo "ABI: 'x86_64'" | cat - $$<.tmp.out | development/scripts/stack | tail -n 3000 ; } ; \
+		rm $$<.tmp.out ; $$(call ART_TEST_FAILED,$$@))
+endif
 
   ART_TEST_HOST_GTEST$$($(3)ART_PHONY_TEST_HOST_SUFFIX)_RULES += $$(gtest_rule)
   ART_TEST_HOST_GTEST_RULES += $$(gtest_rule)
@@ -719,6 +748,8 @@
 $(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_HOST_GTEST_$(dir)_DEX :=))
 ART_TEST_HOST_GTEST_MainStripped_DEX :=
 ART_TEST_TARGET_GTEST_MainStripped_DEX :=
+ART_TEST_HOST_GTEST_MainUncompressed_DEX :=
+ART_TEST_TARGET_GTEST_MainUncompressed_DEX :=
 ART_TEST_GTEST_VerifierDeps_SRC :=
 ART_TEST_HOST_GTEST_VerifierDeps_DEX :=
 ART_TEST_TARGET_GTEST_VerifierDeps_DEX :=
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 70cc07e..5d67206 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -375,7 +375,7 @@
 
 TEST_F(CmdlineParserTest, TestJdwpProviderDefault) {
   const char* opt_args = "-XjdwpProvider:default";
-  EXPECT_SINGLE_PARSE_VALUE(JdwpProvider::kAdbConnection, opt_args, M::JdwpProvider);
+  EXPECT_SINGLE_PARSE_VALUE(JdwpProvider::kInternal, opt_args, M::JdwpProvider);
 }  // TEST_F
 
 TEST_F(CmdlineParserTest, TestJdwpProviderInternal) {
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 2bc7409..d0d6bfd 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -77,10 +77,10 @@
           "Example: -XjdwpProvider:internal for internal jdwp implementation\n"
           "Example: -XjdwpProvider:adbconnection for adb connection mediated jdwp implementation\n"
           "Example: -XjdwpProvider:default for the default jdwp implementation"
-          " (currently adbconnection)\n");
-    } else if (option == "internal") {
+          " (currently internal)\n");
+    } else if (option == "internal" || option == "default") {
       return Result::Success(JdwpProvider::kInternal);
-    } else if (option == "adbconnection" || option == "default") {
+    } else if (option == "adbconnection") {
       return Result::Success(JdwpProvider::kAdbConnection);
     } else if (option == "none") {
       return Result::Success(JdwpProvider::kNone);
diff --git a/compiler/dex/dex_to_dex_compiler.h b/compiler/dex/dex_to_dex_compiler.h
index abd0481..2105a9d 100644
--- a/compiler/dex/dex_to_dex_compiler.h
+++ b/compiler/dex/dex_to_dex_compiler.h
@@ -23,8 +23,8 @@
 
 #include "base/bit_vector.h"
 #include "dex/dex_file.h"
+#include "dex/invoke_type.h"
 #include "handle.h"
-#include "invoke_type.h"
 #include "method_reference.h"
 #include "quicken_info.h"
 
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 70cbb01..3720dda 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -384,6 +384,12 @@
     Thread* self, const CompilerDriver& driver, Handle<mirror::ClassLoader> class_loader,
     const DexFile& dex_file, const DexFile::ClassDef& class_def)
     REQUIRES_SHARED(Locks::mutator_lock_) {
+  // When the dex file is uncompressed in the APK, we do not generate a copy in the .vdex
+  // file. As a result, dex2oat will map the dex file read-only, and we only need to check
+  // that to know if we can do quickening.
+  if (dex_file.GetContainer() != nullptr && dex_file.GetContainer()->IsReadOnly()) {
+    return optimizer::DexToDexCompiler::CompilationLevel::kDontDexToDexCompile;
+  }
   auto* const runtime = Runtime::Current();
   DCHECK(driver.GetCompilerOptions().IsQuickeningCompilationEnabled());
   const char* descriptor = dex_file.GetClassDescriptor(class_def);
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 4b5916d..18b1e0e 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -110,13 +110,13 @@
 
   ~CompilerDriver();
 
-  // Set dex files that will be stored in the oat file after being compiled.
+  // Set dex files associated with the oat file being compiled.
   void SetDexFilesForOatFile(const std::vector<const DexFile*>& dex_files);
 
   // Set dex files classpath.
   void SetClasspathDexFiles(const std::vector<const DexFile*>& dex_files);
 
-  // Get dex file that will be stored in the oat file after being compiled.
+  // Get dex files associated with the the oat file being compiled.
   ArrayRef<const DexFile* const> GetDexFilesForOatFile() const {
     return ArrayRef<const DexFile* const>(dex_files_for_oat_file_);
   }
@@ -533,7 +533,7 @@
 
   bool support_boot_image_fixup_;
 
-  // List of dex files that will be stored in the oat file.
+  // List of dex files associates with the oat file.
   std::vector<const DexFile*> dex_files_for_oat_file_;
 
   CompiledMethodStorage compiled_method_storage_;
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 84ba178..504c647 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -2490,8 +2490,12 @@
   }
 
   if (!skip_overflow_check) {
-    UseScratchRegisterScope temps(GetVIXLAssembler());
-    vixl32::Register temp = temps.Acquire();
+    // Using r4 instead of IP saves 2 bytes. Start by asserting that r4 is available here.
+    for (vixl32::Register reg : kParameterCoreRegistersVIXL) {
+      DCHECK(!reg.Is(r4));
+    }
+    DCHECK(!kCoreCalleeSaves.Includes(r4));
+    vixl32::Register temp = r4;
     __ Sub(temp, sp, Operand::From(GetStackOverflowReservedBytes(InstructionSet::kArm)));
     // The load must immediately precede RecordPcInfo.
     ExactAssemblyScope aas(GetVIXLAssembler(),
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index e81d97b..02465d3 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -18,7 +18,7 @@
 #define ART_COMPILER_OPTIMIZING_INLINER_H_
 
 #include "dex/dex_file_types.h"
-#include "invoke_type.h"
+#include "dex/invoke_type.h"
 #include "jit/profile_compilation_info.h"
 #include "optimization.h"
 
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 6928b70..acb830e 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -19,9 +19,9 @@
 #include "art_field-inl.h"
 #include "art_method-inl.h"
 #include "class_linker.h"
+#include "dex/invoke_type.h"
 #include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
-#include "invoke_type.h"
 #include "mirror/dex_cache-inl.h"
 #include "nodes.h"
 #include "scoped_thread_state_change-inl.h"
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index aae94b2..88326d3 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -25,51 +25,6 @@
 
 #include <iostream>
 
-/**
- * The general algorithm of load-store elimination (LSE).
- * Load-store analysis in the previous pass collects a list of heap locations
- * and does alias analysis of those heap locations.
- * LSE keeps track of a list of heap values corresponding to the heap
- * locations. It visits basic blocks in reverse post order and for
- * each basic block, visits instructions sequentially, and processes
- * instructions as follows:
- * - If the instruction is a load, and the heap location for that load has a
- *   valid heap value, the load can be eliminated. In order to maintain the
- *   validity of all heap locations during the optimization phase, the real
- *   elimination is delayed till the end of LSE.
- * - If the instruction is a store, it updates the heap value for the heap
- *   location of the store with the store instruction. The real heap value
- *   can be fetched from the store instruction. Heap values are invalidated
- *   for heap locations that may alias with the store instruction's heap
- *   location. The store instruction can be eliminated unless the value stored
- *   is later needed e.g. by a load from the same/aliased heap location or
- *   the heap location persists at method return/deoptimization.
- *   The store instruction is also needed if it's not used to track the heap
- *   value anymore, e.g. when it fails to merge with the heap values from other
- *   predecessors.
- * - A store that stores the same value as the heap value is eliminated.
- * - The list of heap values are merged at basic block entry from the basic
- *   block's predecessors. The algorithm is single-pass, so loop side-effects is
- *   used as best effort to decide if a heap location is stored inside the loop.
- * - A special type of objects called singletons are instantiated in the method
- *   and have a single name, i.e. no aliases. Singletons have exclusive heap
- *   locations since they have no aliases. Singletons are helpful in narrowing
- *   down the life span of a heap location such that they do not always
- *   need to participate in merging heap values. Allocation of a singleton
- *   can be eliminated if that singleton is not used and does not persist
- *   at method return/deoptimization.
- * - For newly instantiated instances, their heap values are initialized to
- *   language defined default values.
- * - Some instructions such as invokes are treated as loading and invalidating
- *   all the heap values, depending on the instruction's side effects.
- * - Finalizable objects are considered as persisting at method
- *   return/deoptimization.
- * - Currently this LSE algorithm doesn't handle SIMD graph, e.g. with VecLoad
- *   and VecStore instructions.
- * - Currently this LSE algorithm doesn't handle graph with try-catch, due to
- *   the special block merging structure.
- */
-
 namespace art {
 
 // An unknown heap value. Loads with such a value in the heap location cannot be eliminated.
@@ -104,7 +59,8 @@
         removed_loads_(allocator_.Adapter(kArenaAllocLSE)),
         substitute_instructions_for_loads_(allocator_.Adapter(kArenaAllocLSE)),
         possibly_removed_stores_(allocator_.Adapter(kArenaAllocLSE)),
-        singleton_new_instances_(allocator_.Adapter(kArenaAllocLSE)) {
+        singleton_new_instances_(allocator_.Adapter(kArenaAllocLSE)),
+        singleton_new_arrays_(allocator_.Adapter(kArenaAllocLSE)) {
   }
 
   void VisitBasicBlock(HBasicBlock* block) OVERRIDE {
@@ -132,26 +88,19 @@
     return type_conversion;
   }
 
-  // Find an instruction's substitute if it's a removed load.
+  // Find an instruction's substitute if it should be removed.
   // Return the same instruction if it should not be removed.
   HInstruction* FindSubstitute(HInstruction* instruction) {
-    if (!IsLoad(instruction)) {
-      return instruction;
-    }
     size_t size = removed_loads_.size();
     for (size_t i = 0; i < size; i++) {
       if (removed_loads_[i] == instruction) {
-        HInstruction* substitute = substitute_instructions_for_loads_[i];
-        // The substitute list is a flat hierarchy.
-        DCHECK_EQ(FindSubstitute(substitute), substitute);
-        return substitute;
+        return substitute_instructions_for_loads_[i];
       }
     }
     return instruction;
   }
 
   void AddRemovedLoad(HInstruction* load, HInstruction* heap_value) {
-    DCHECK(IsLoad(load));
     DCHECK_EQ(FindSubstitute(heap_value), heap_value) <<
         "Unexpected heap_value that has a substitute " << heap_value->DebugName();
     removed_loads_.push_back(load);
@@ -258,59 +207,28 @@
         new_instance->GetBlock()->RemoveInstruction(new_instance);
       }
     }
+    for (HInstruction* new_array : singleton_new_arrays_) {
+      size_t removed = HConstructorFence::RemoveConstructorFences(new_array);
+      MaybeRecordStat(stats_,
+                      MethodCompilationStat::kConstructorFenceRemovedLSE,
+                      removed);
+
+      if (!new_array->HasNonEnvironmentUses()) {
+        new_array->RemoveEnvironmentUsers();
+        new_array->GetBlock()->RemoveInstruction(new_array);
+      }
+    }
   }
 
  private:
-  static bool IsLoad(HInstruction* instruction) {
-    if (instruction == kUnknownHeapValue || instruction == kDefaultHeapValue) {
-      return false;
-    }
-    // Unresolved load is not treated as a load.
-    return instruction->IsInstanceFieldGet() ||
-        instruction->IsStaticFieldGet() ||
-        instruction->IsArrayGet();
-  }
-
-  static bool IsStore(HInstruction* instruction) {
-    if (instruction == kUnknownHeapValue || instruction == kDefaultHeapValue) {
-      return false;
-    }
-    // Unresolved store is not treated as a store.
-    return instruction->IsInstanceFieldSet() ||
-        instruction->IsArraySet() ||
-        instruction->IsStaticFieldSet();
-  }
-
-  // Returns the real heap value by finding its substitute or by "peeling"
-  // a store instruction.
-  HInstruction* GetRealHeapValue(HInstruction* heap_value) {
-    if (IsLoad(heap_value)) {
-      return FindSubstitute(heap_value);
-    }
-    if (!IsStore(heap_value)) {
-      return heap_value;
-    }
-
-    // We keep track of store instructions as the heap values which might be
-    // eliminated if the stores are later found not necessary. The real stored
-    // value needs to be fetched from the store instruction.
-    if (heap_value->IsInstanceFieldSet()) {
-      heap_value = heap_value->AsInstanceFieldSet()->GetValue();
-    } else if (heap_value->IsStaticFieldSet()) {
-      heap_value = heap_value->AsStaticFieldSet()->GetValue();
-    } else {
-      DCHECK(heap_value->IsArraySet());
-      heap_value = heap_value->AsArraySet()->GetValue();
-    }
-    // heap_value may already be a removed load.
-    return FindSubstitute(heap_value);
-  }
-
-  // If heap_value is a store, need to keep the store.
-  // This is necessary if a heap value is killed or replaced by another value,
-  // so that the store is no longer used to track heap value.
+  // If heap_values[index] is an instance field store, need to keep the store.
+  // This is necessary if a heap value is killed due to merging, or loop side
+  // effects (which is essentially merging also), since a load later from the
+  // location won't be eliminated.
   void KeepIfIsStore(HInstruction* heap_value) {
-    if (!IsStore(heap_value)) {
+    if (heap_value == kDefaultHeapValue ||
+        heap_value == kUnknownHeapValue ||
+        !(heap_value->IsInstanceFieldSet() || heap_value->IsArraySet())) {
       return;
     }
     auto idx = std::find(possibly_removed_stores_.begin(),
@@ -321,41 +239,26 @@
     }
   }
 
-  // If a heap location X may alias with heap location at `loc_index`
-  // and heap_values of that heap location X holds a store, keep that store.
-  // It's needed for a dependent load that's not eliminated since any store
-  // that may put value into the load's heap location needs to be kept.
-  void KeepStoresIfAliasedToLocation(ScopedArenaVector<HInstruction*>& heap_values,
-                                     size_t loc_index) {
-    for (size_t i = 0; i < heap_values.size(); i++) {
-      if ((i == loc_index) || heap_location_collector_.MayAlias(i, loc_index)) {
-        KeepIfIsStore(heap_values[i]);
-      }
-    }
-  }
-
   void HandleLoopSideEffects(HBasicBlock* block) {
     DCHECK(block->IsLoopHeader());
     int block_id = block->GetBlockId();
     ScopedArenaVector<HInstruction*>& heap_values = heap_values_for_[block_id];
-    HBasicBlock* pre_header = block->GetLoopInformation()->GetPreHeader();
-    ScopedArenaVector<HInstruction*>& pre_header_heap_values =
-        heap_values_for_[pre_header->GetBlockId()];
 
-    // Don't eliminate loads in irreducible loops.
-    // Also keep the stores before the loop.
+    // Don't eliminate loads in irreducible loops. This is safe for singletons, because
+    // they are always used by the non-eliminated loop-phi.
     if (block->GetLoopInformation()->IsIrreducible()) {
       if (kIsDebugBuild) {
         for (size_t i = 0; i < heap_values.size(); i++) {
           DCHECK_EQ(heap_values[i], kUnknownHeapValue);
         }
       }
-      for (size_t i = 0; i < heap_values.size(); i++) {
-        KeepIfIsStore(pre_header_heap_values[i]);
-      }
       return;
     }
 
+    HBasicBlock* pre_header = block->GetLoopInformation()->GetPreHeader();
+    ScopedArenaVector<HInstruction*>& pre_header_heap_values =
+        heap_values_for_[pre_header->GetBlockId()];
+
     // Inherit the values from pre-header.
     for (size_t i = 0; i < heap_values.size(); i++) {
       heap_values[i] = pre_header_heap_values[i];
@@ -367,17 +270,18 @@
       for (size_t i = 0; i < heap_values.size(); i++) {
         HeapLocation* location = heap_location_collector_.GetHeapLocation(i);
         ReferenceInfo* ref_info = location->GetReferenceInfo();
-        if (ref_info->IsSingleton() && !location->IsValueKilledByLoopSideEffects()) {
-          // A singleton's field that's not stored into inside a loop is
+        if (ref_info->IsSingletonAndRemovable() &&
+            !location->IsValueKilledByLoopSideEffects()) {
+          // A removable singleton's field that's not stored into inside a loop is
           // invariant throughout the loop. Nothing to do.
         } else {
-          // heap value is killed by loop side effects.
+          // heap value is killed by loop side effects (stored into directly, or
+          // due to aliasing). Or the heap value may be needed after method return
+          // or deoptimization.
           KeepIfIsStore(pre_header_heap_values[i]);
           heap_values[i] = kUnknownHeapValue;
         }
       }
-    } else {
-      // The loop doesn't kill any value.
     }
   }
 
@@ -396,73 +300,45 @@
     ScopedArenaVector<HInstruction*>& heap_values = heap_values_for_[block->GetBlockId()];
     for (size_t i = 0; i < heap_values.size(); i++) {
       HInstruction* merged_value = nullptr;
-      // If we can merge the store itself from the predecessors, we keep
-      // the store as the heap value as long as possible. In case we cannot
-      // merge the store, we try to merge the values of the stores.
-      HInstruction* merged_store_value = nullptr;
       // Whether merged_value is a result that's merged from all predecessors.
       bool from_all_predecessors = true;
       ReferenceInfo* ref_info = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo();
-      HInstruction* ref = ref_info->GetReference();
       HInstruction* singleton_ref = nullptr;
       if (ref_info->IsSingleton()) {
-        // We do more analysis based on singleton's liveness when merging
-        // heap values for such cases.
-        singleton_ref = ref;
+        // We do more analysis of liveness when merging heap values for such
+        // cases since stores into such references may potentially be eliminated.
+        singleton_ref = ref_info->GetReference();
       }
 
       for (HBasicBlock* predecessor : predecessors) {
         HInstruction* pred_value = heap_values_for_[predecessor->GetBlockId()][i];
-        if (!IsStore(pred_value)) {
-          pred_value = FindSubstitute(pred_value);
-        }
-        DCHECK(pred_value != nullptr);
-        HInstruction* pred_store_value = GetRealHeapValue(pred_value);
         if ((singleton_ref != nullptr) &&
             !singleton_ref->GetBlock()->Dominates(predecessor)) {
-          // singleton_ref is not live in this predecessor. No need to merge
-          // since singleton_ref is not live at the beginning of this block.
+          // singleton_ref is not live in this predecessor. Skip this predecessor since
+          // it does not really have the location.
           DCHECK_EQ(pred_value, kUnknownHeapValue);
           from_all_predecessors = false;
-          break;
+          continue;
         }
         if (merged_value == nullptr) {
           // First seen heap value.
-          DCHECK(pred_value != nullptr);
           merged_value = pred_value;
         } else if (pred_value != merged_value) {
           // There are conflicting values.
           merged_value = kUnknownHeapValue;
-          // We may still be able to merge store values.
-        }
-
-        // Conflicting stores may be storing the same value. We do another merge
-        // of real stored values.
-        if (merged_store_value == nullptr) {
-          // First seen store value.
-          DCHECK(pred_store_value != nullptr);
-          merged_store_value = pred_store_value;
-        } else if (pred_store_value != merged_store_value) {
-          // There are conflicting store values.
-          merged_store_value = kUnknownHeapValue;
-          // There must be conflicting stores also.
-          DCHECK_EQ(merged_value, kUnknownHeapValue);
-          // No need to merge anymore.
           break;
         }
       }
 
-      if (merged_value == nullptr) {
-        DCHECK(!from_all_predecessors);
-        DCHECK(singleton_ref != nullptr);
-      }
-      if (from_all_predecessors) {
-        if (ref_info->IsSingletonAndRemovable() &&
-            block->IsSingleReturnOrReturnVoidAllowingPhis()) {
-          // Values in the singleton are not needed anymore.
-        } else if (!IsStore(merged_value)) {
-          // We don't track merged value as a store anymore. We have to
-          // hold the stores in predecessors live here.
+      if (ref_info->IsSingleton()) {
+        if (ref_info->IsSingletonAndNonRemovable() ||
+            (merged_value == kUnknownHeapValue &&
+             !block->IsSingleReturnOrReturnVoidAllowingPhis())) {
+          // The heap value may be needed after method return or deoptimization,
+          // or there are conflicting heap values from different predecessors and
+          // this block is not a single return,
+          // keep the last store in each predecessor since future loads may not
+          // be eliminated.
           for (HBasicBlock* predecessor : predecessors) {
             ScopedArenaVector<HInstruction*>& pred_values =
                 heap_values_for_[predecessor->GetBlockId()];
@@ -470,33 +346,18 @@
           }
         }
       } else {
-        DCHECK(singleton_ref != nullptr);
-        // singleton_ref is non-existing at the beginning of the block. There is
-        // no need to keep the stores.
+        // Currenctly we don't eliminate stores to non-singletons.
       }
 
-      if (!from_all_predecessors) {
+      if ((merged_value == nullptr) || !from_all_predecessors) {
         DCHECK(singleton_ref != nullptr);
         DCHECK((singleton_ref->GetBlock() == block) ||
-               !singleton_ref->GetBlock()->Dominates(block))
-            << "method: " << GetGraph()->GetMethodName();
+               !singleton_ref->GetBlock()->Dominates(block));
         // singleton_ref is not defined before block or defined only in some of its
         // predecessors, so block doesn't really have the location at its entry.
         heap_values[i] = kUnknownHeapValue;
-      } else if (predecessors.size() == 1) {
-        // Inherit heap value from the single predecessor.
-        DCHECK_EQ(heap_values_for_[predecessors[0]->GetBlockId()][i], merged_value);
-        heap_values[i] = merged_value;
       } else {
-        DCHECK(merged_value == kUnknownHeapValue ||
-               merged_value == kDefaultHeapValue ||
-               merged_value->GetBlock()->Dominates(block));
-        if (merged_value != kUnknownHeapValue) {
-          heap_values[i] = merged_value;
-        } else {
-          // Stores in different predecessors may be storing the same value.
-          heap_values[i] = merged_store_value;
-        }
+        heap_values[i] = merged_value;
       }
     }
   }
@@ -562,12 +423,23 @@
       heap_values[idx] = constant;
       return;
     }
-    heap_value = GetRealHeapValue(heap_value);
+    if (heap_value != kUnknownHeapValue) {
+      if (heap_value->IsInstanceFieldSet() || heap_value->IsArraySet()) {
+        HInstruction* store = heap_value;
+        // This load must be from a singleton since it's from the same
+        // field/element that a "removed" store puts the value. That store
+        // must be to a singleton's field/element.
+        DCHECK(ref_info->IsSingleton());
+        // Get the real heap value of the store.
+        heap_value = heap_value->IsInstanceFieldSet() ? store->InputAt(1) : store->InputAt(2);
+        // heap_value may already have a substitute.
+        heap_value = FindSubstitute(heap_value);
+      }
+    }
     if (heap_value == kUnknownHeapValue) {
       // Load isn't eliminated. Put the load as the value into the HeapLocation.
       // This acts like GVN but with better aliasing analysis.
       heap_values[idx] = instruction;
-      KeepStoresIfAliasedToLocation(heap_values, idx);
     } else {
       if (DataType::Kind(heap_value->GetType()) != DataType::Kind(instruction->GetType())) {
         // The only situation where the same heap location has different type is when
@@ -580,10 +452,6 @@
           DCHECK(heap_value->IsArrayGet()) << heap_value->DebugName();
           DCHECK(instruction->IsArrayGet()) << instruction->DebugName();
         }
-        // Load isn't eliminated. Put the load as the value into the HeapLocation.
-        // This acts like GVN but with better aliasing analysis.
-        heap_values[idx] = instruction;
-        KeepStoresIfAliasedToLocation(heap_values, idx);
         return;
       }
       AddRemovedLoad(instruction, heap_value);
@@ -592,21 +460,12 @@
   }
 
   bool Equal(HInstruction* heap_value, HInstruction* value) {
-    DCHECK(!IsStore(value)) << value->DebugName();
-    if (heap_value == kUnknownHeapValue) {
-      // Don't compare kUnknownHeapValue with other values.
-      return false;
-    }
     if (heap_value == value) {
       return true;
     }
     if (heap_value == kDefaultHeapValue && GetDefaultValue(value->GetType()) == value) {
       return true;
     }
-    HInstruction* real_heap_value = GetRealHeapValue(heap_value);
-    if (real_heap_value != heap_value) {
-      return Equal(real_heap_value, value);
-    }
     return false;
   }
 
@@ -617,7 +476,6 @@
                         size_t vector_length,
                         int16_t declaring_class_def_index,
                         HInstruction* value) {
-    DCHECK(!IsStore(value)) << value->DebugName();
     // value may already have a substitute.
     value = FindSubstitute(value);
     HInstruction* original_ref = heap_location_collector_.HuntForOriginalReference(ref);
@@ -628,47 +486,59 @@
     ScopedArenaVector<HInstruction*>& heap_values =
         heap_values_for_[instruction->GetBlock()->GetBlockId()];
     HInstruction* heap_value = heap_values[idx];
+    bool same_value = false;
     bool possibly_redundant = false;
-
     if (Equal(heap_value, value)) {
       // Store into the heap location with the same value.
-      // This store can be eliminated right away.
-      instruction->GetBlock()->RemoveInstruction(instruction);
-      return;
-    } else {
+      same_value = true;
+    } else if (index != nullptr &&
+               heap_location_collector_.GetHeapLocation(idx)->HasAliasedLocations()) {
+      // For array element, don't eliminate stores if the location can be aliased
+      // (due to either ref or index aliasing).
+    } else if (ref_info->IsSingleton()) {
+      // Store into a field/element of a singleton. The value cannot be killed due to
+      // aliasing/invocation. It can be redundant since future loads can
+      // directly get the value set by this instruction. The value can still be killed due to
+      // merging or loop side effects. Stores whose values are killed due to merging/loop side
+      // effects later will be removed from possibly_removed_stores_ when that is detected.
+      // Stores whose values may be needed after method return or deoptimization
+      // are also removed from possibly_removed_stores_ when that is detected.
+      possibly_redundant = true;
       HLoopInformation* loop_info = instruction->GetBlock()->GetLoopInformation();
-      if (loop_info == nullptr) {
-        // Store is not in a loop. We try to precisely track the heap value by
-        // the store.
-        possibly_redundant = true;
-      } else if (!loop_info->IsIrreducible()) {
-        // instruction is a store in the loop so the loop must do write.
+      if (loop_info != nullptr) {
+        // instruction is a store in the loop so the loop must does write.
         DCHECK(side_effects_.GetLoopEffects(loop_info->GetHeader()).DoesAnyWrite());
-        if (ref_info->IsSingleton() && !loop_info->IsDefinedOutOfTheLoop(original_ref)) {
-          // original_ref is created inside the loop. Value stored to it isn't needed at
-          // the loop header. This is true for outer loops also.
-          possibly_redundant = true;
-        } else {
+
+        if (loop_info->IsDefinedOutOfTheLoop(original_ref)) {
+          DCHECK(original_ref->GetBlock()->Dominates(loop_info->GetPreHeader()));
           // Keep the store since its value may be needed at the loop header.
+          possibly_redundant = false;
+        } else {
+          // The singleton is created inside the loop. Value stored to it isn't needed at
+          // the loop header. This is true for outer loops also.
         }
-      } else {
-        // Keep the store inside irreducible loops.
       }
     }
-    if (possibly_redundant) {
+    if (same_value || possibly_redundant) {
       possibly_removed_stores_.push_back(instruction);
     }
 
-    // Put the store as the heap value. If the value is loaded or needed after
-    // return/deoptimization later, this store isn't really redundant.
-    heap_values[idx] = instruction;
-
+    if (!same_value) {
+      if (possibly_redundant) {
+        DCHECK(instruction->IsInstanceFieldSet() || instruction->IsArraySet());
+        // Put the store as the heap value. If the value is loaded from heap
+        // by a load later, this store isn't really redundant.
+        heap_values[idx] = instruction;
+      } else {
+        heap_values[idx] = value;
+      }
+    }
     // This store may kill values in other heap locations due to aliasing.
     for (size_t i = 0; i < heap_values.size(); i++) {
       if (i == idx) {
         continue;
       }
-      if (Equal(heap_values[i], value)) {
+      if (heap_values[i] == value) {
         // Same value should be kept even if aliasing happens.
         continue;
       }
@@ -677,9 +547,7 @@
         continue;
       }
       if (heap_location_collector_.MayAlias(i, idx)) {
-        // Kill heap locations that may alias and as a result if the heap value
-        // is a store, the store needs to be kept.
-        KeepIfIsStore(heap_values[i]);
+        // Kill heap locations that may alias.
         heap_values[i] = kUnknownHeapValue;
       }
     }
@@ -765,35 +633,24 @@
     const ScopedArenaVector<HInstruction*>& heap_values =
         heap_values_for_[instruction->GetBlock()->GetBlockId()];
     for (HInstruction* heap_value : heap_values) {
+      // Filter out fake instructions before checking instruction kind below.
+      if (heap_value == kUnknownHeapValue || heap_value == kDefaultHeapValue) {
+        continue;
+      }
       // A store is kept as the heap value for possibly removed stores.
-      // That value stored is generally observeable after deoptimization, except
-      // for singletons that don't escape after deoptimization.
-      if (IsStore(heap_value)) {
-        if (heap_value->IsStaticFieldSet()) {
-          KeepIfIsStore(heap_value);
-          continue;
-        }
+      if (heap_value->IsInstanceFieldSet() || heap_value->IsArraySet()) {
+        // Check whether the reference for a store is used by an environment local of
+        // HDeoptimize.
         HInstruction* reference = heap_value->InputAt(0);
-        if (heap_location_collector_.FindReferenceInfoOf(reference)->IsSingleton()) {
-          if (reference->IsNewInstance() && reference->AsNewInstance()->IsFinalizable()) {
-            // Finalizable objects alway escape.
+        DCHECK(heap_location_collector_.FindReferenceInfoOf(reference)->IsSingleton());
+        for (const HUseListNode<HEnvironment*>& use : reference->GetEnvUses()) {
+          HEnvironment* user = use.GetUser();
+          if (user->GetHolder() == instruction) {
+            // The singleton for the store is visible at this deoptimization
+            // point. Need to keep the store so that the heap value is
+            // seen by the interpreter.
             KeepIfIsStore(heap_value);
-            continue;
           }
-          // Check whether the reference for a store is used by an environment local of
-          // HDeoptimize. If not, the singleton is not observed after
-          // deoptimizion.
-          for (const HUseListNode<HEnvironment*>& use : reference->GetEnvUses()) {
-            HEnvironment* user = use.GetUser();
-            if (user->GetHolder() == instruction) {
-              // The singleton for the store is visible at this deoptimization
-              // point. Need to keep the store so that the heap value is
-              // seen by the interpreter.
-              KeepIfIsStore(heap_value);
-            }
-          }
-        } else {
-          KeepIfIsStore(heap_value);
         }
       }
     }
@@ -901,7 +758,7 @@
       return;
     }
     if (ref_info->IsSingletonAndRemovable()) {
-      singleton_new_instances_.push_back(new_array);
+      singleton_new_arrays_.push_back(new_array);
     }
     ScopedArenaVector<HInstruction*>& heap_values =
         heap_values_for_[new_array->GetBlock()->GetBlockId()];
@@ -934,6 +791,7 @@
   ScopedArenaVector<HInstruction*> possibly_removed_stores_;
 
   ScopedArenaVector<HInstruction*> singleton_new_instances_;
+  ScopedArenaVector<HInstruction*> singleton_new_arrays_;
 
   DISALLOW_COPY_AND_ASSIGN(LSEVisitor);
 };
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index b0657d6..a9782a6 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -32,11 +32,11 @@
 #include "deoptimization_kind.h"
 #include "dex/dex_file.h"
 #include "dex/dex_file_types.h"
+#include "dex/invoke_type.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "handle.h"
 #include "handle_scope.h"
 #include "intrinsics_enum.h"
-#include "invoke_type.h"
 #include "locations.h"
 #include "method_reference.h"
 #include "mirror/class.h"
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 34ba4b3..8555abf 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1548,7 +1548,7 @@
       for (size_t i = 0, size = oat_writers_.size(); i != size; ++i) {
         rodata_.push_back(elf_writers_[i]->StartRoData());
         // Unzip or copy dex files straight to the oat file.
-        std::unique_ptr<MemMap> opened_dex_files_map;
+        std::vector<std::unique_ptr<MemMap>> opened_dex_files_map;
         std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
         // No need to verify the dex file for:
         // 1) Dexlayout since it does the verification. It also may not pass the verification since
@@ -1568,14 +1568,16 @@
           return dex2oat::ReturnCode::kOther;
         }
         dex_files_per_oat_file_.push_back(MakeNonOwningPointerVector(opened_dex_files));
-        if (opened_dex_files_map != nullptr) {
-          opened_dex_files_maps_.push_back(std::move(opened_dex_files_map));
+        if (opened_dex_files_map.empty()) {
+          DCHECK(opened_dex_files.empty());
+        } else {
+          for (std::unique_ptr<MemMap>& map : opened_dex_files_map) {
+            opened_dex_files_maps_.push_back(std::move(map));
+          }
           for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files) {
             dex_file_oat_index_map_.emplace(dex_file.get(), i);
             opened_dex_files_.push_back(std::move(dex_file));
           }
-        } else {
-          DCHECK(opened_dex_files.empty());
         }
       }
     }
@@ -2402,7 +2404,7 @@
 
   bool AddDexFileSources() {
     TimingLogger::ScopedTiming t2("AddDexFileSources", timings_);
-    if (input_vdex_file_ != nullptr) {
+    if (input_vdex_file_ != nullptr && input_vdex_file_->HasDexSection()) {
       DCHECK_EQ(oat_writers_.size(), 1u);
       const std::string& name = zip_location_.empty() ? dex_locations_[0] : zip_location_;
       DCHECK(!name.empty());
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 8799540..6fcf695 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -1136,7 +1136,7 @@
   std::string expected_classpath_key;
   {
     // Open the oat file to get the expected classpath.
-    OatFileAssistant oat_file_assistant(stripped_classpath.c_str(), kRuntimeISA, false);
+    OatFileAssistant oat_file_assistant(stripped_classpath.c_str(), kRuntimeISA, false, false);
     std::unique_ptr<OatFile> oat_file(oat_file_assistant.GetBestOatFile());
     std::vector<std::unique_ptr<const DexFile>> oat_dex_files =
         OatFileAssistant::LoadDexFiles(*oat_file, stripped_classpath.c_str());
@@ -1528,4 +1528,19 @@
   EXPECT_LT(dedupe_size, no_dedupe_size);
 }
 
+TEST_F(Dex2oatTest, UncompressedTest) {
+  std::unique_ptr<const DexFile> dex(OpenTestDexFile("MainUncompressed"));
+  std::string out_dir = GetScratchDir();
+  const std::string base_oat_name = out_dir + "/base.oat";
+  GenerateOdexForTest(dex->GetLocation(),
+                      base_oat_name,
+                      CompilerFilter::Filter::kQuicken,
+                      { },
+                      true,  // expect_success
+                      false,  // use_fd
+                      [](const OatFile& o) {
+                        CHECK(!o.ContainsDexCode());
+                      });
+}
+
 }  // namespace art
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 637578e..62519fc 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -253,7 +253,7 @@
       }
 
       std::vector<OutputStream*> rodata;
-      std::vector<std::unique_ptr<MemMap>> opened_dex_files_map;
+      std::vector<std::unique_ptr<MemMap>> opened_dex_files_maps;
       std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
       // Now that we have finalized key_value_store_, start writing the oat file.
       for (size_t i = 0, size = oat_writers.size(); i != size; ++i) {
@@ -266,7 +266,7 @@
                                             dex_file->GetLocation().c_str(),
                                             dex_file->GetLocationChecksum());
 
-        std::unique_ptr<MemMap> cur_opened_dex_files_map;
+        std::vector<std::unique_ptr<MemMap>> cur_opened_dex_files_maps;
         std::vector<std::unique_ptr<const DexFile>> cur_opened_dex_files;
         bool dex_files_ok = oat_writers[i]->WriteAndOpenDexFiles(
             vdex_files[i].GetFile(),
@@ -276,12 +276,14 @@
             &key_value_store,
             /* verify */ false,           // Dex files may be dex-to-dex-ed, don't verify.
             /* update_input_vdex */ false,
-            &cur_opened_dex_files_map,
+            &cur_opened_dex_files_maps,
             &cur_opened_dex_files);
         ASSERT_TRUE(dex_files_ok);
 
-        if (cur_opened_dex_files_map != nullptr) {
-          opened_dex_files_map.push_back(std::move(cur_opened_dex_files_map));
+        if (!cur_opened_dex_files_maps.empty()) {
+          for (std::unique_ptr<MemMap>& cur_map : cur_opened_dex_files_maps) {
+            opened_dex_files_maps.push_back(std::move(cur_map));
+          }
           for (std::unique_ptr<const DexFile>& cur_dex_file : cur_opened_dex_files) {
             // dex_file_oat_index_map_.emplace(dex_file.get(), i);
             opened_dex_files.push_back(std::move(cur_dex_file));
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index f790db2..b3a92e5 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -39,6 +39,7 @@
 #include "dex/dex_file_types.h"
 #include "dex/standard_dex_file.h"
 #include "dex/verification_results.h"
+#include "dex_container.h"
 #include "dexlayout.h"
 #include "driver/compiler_driver-inl.h"
 #include "driver/compiler_options.h"
@@ -367,6 +368,7 @@
     compiler_driver_(nullptr),
     image_writer_(nullptr),
     compiling_boot_image_(compiling_boot_image),
+    only_contains_uncompressed_zip_entries_(false),
     dex_files_(nullptr),
     vdex_size_(0u),
     vdex_dex_files_offset_(0u),
@@ -640,7 +642,7 @@
     SafeMap<std::string, std::string>* key_value_store,
     bool verify,
     bool update_input_vdex,
-    /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
+    /*out*/ std::vector<std::unique_ptr<MemMap>>* opened_dex_files_map,
     /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
   CHECK(write_state_ == WriteState::kAddingDexFileSources);
 
@@ -649,7 +651,7 @@
      return false;
   }
 
-  std::unique_ptr<MemMap> dex_files_map;
+  std::vector<std::unique_ptr<MemMap>> dex_files_map;
   std::vector<std::unique_ptr<const DexFile>> dex_files;
 
   // Initialize VDEX and OAT headers.
@@ -2746,6 +2748,12 @@
 };
 
 bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) {
+  if (only_contains_uncompressed_zip_entries_) {
+    // Nothing to write. Leave `vdex_size_` untouched and unaligned.
+    vdex_quickening_info_offset_ = vdex_size_;
+    size_quickening_info_alignment_ = 0;
+    return true;
+  }
   size_t initial_offset = vdex_size_;
   // Make sure the table is properly aligned.
   size_t start_offset = RoundUp(initial_offset, 4u);
@@ -3335,14 +3343,28 @@
 
   vdex_dex_files_offset_ = vdex_size_;
 
-  // Write dex files.
+  only_contains_uncompressed_zip_entries_ = true;
   for (OatDexFile& oat_dex_file : oat_dex_files_) {
-    if (!WriteDexFile(out, file, &oat_dex_file, update_input_vdex)) {
-      return false;
+    if (!oat_dex_file.source_.IsZipEntry()) {
+      only_contains_uncompressed_zip_entries_ = false;
+      break;
+    }
+    ZipEntry* entry = oat_dex_file.source_.GetZipEntry();
+    if (!entry->IsUncompressed() || !entry->IsAlignedToDexHeader()) {
+      only_contains_uncompressed_zip_entries_ = false;
+      break;
     }
   }
 
-  CloseSources();
+  if (!only_contains_uncompressed_zip_entries_) {
+    // Write dex files.
+    for (OatDexFile& oat_dex_file : oat_dex_files_) {
+      if (!WriteDexFile(out, file, &oat_dex_file, update_input_vdex)) {
+        return false;
+      }
+    }
+  }
+
   return true;
 }
 
@@ -3496,18 +3518,21 @@
     return false;
   }
   Options options;
-  options.output_to_memmap_ = true;
   options.compact_dex_level_ = compact_dex_level_;
   options.update_checksum_ = true;
-  DexLayout dex_layout(options, profile_compilation_info_, nullptr);
-  dex_layout.ProcessDexFile(location.c_str(), dex_file.get(), 0);
-  std::unique_ptr<MemMap> mem_map(dex_layout.GetAndReleaseMemMap());
+  DexLayout dex_layout(options, profile_compilation_info_, /*file*/ nullptr, /*header*/ nullptr);
+  std::unique_ptr<DexContainer> out_data;
+  dex_layout.ProcessDexFile(location.c_str(), dex_file.get(), 0, &out_data);
   oat_dex_file->dex_sections_layout_ = dex_layout.GetSections();
   // Dex layout can affect the size of the dex file, so we update here what we have set
   // when adding the dex file as a source.
-  const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(mem_map->Begin());
+  const UnalignedDexFileHeader* header =
+      AsUnalignedDexFileHeader(out_data->GetMainSection()->Begin());
   oat_dex_file->dex_file_size_ = header->file_size_;
-  if (!WriteDexFile(out, oat_dex_file, mem_map->Begin(), /* update_input_vdex */ false)) {
+  if (!WriteDexFile(out,
+                    oat_dex_file,
+                    out_data->GetMainSection()->Begin(),
+                    /* update_input_vdex */ false)) {
     return false;
   }
   CHECK_EQ(oat_dex_file->dex_file_location_checksum_, dex_file->GetLocationChecksum());
@@ -3682,7 +3707,7 @@
 bool OatWriter::OpenDexFiles(
     File* file,
     bool verify,
-    /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
+    /*out*/ std::vector<std::unique_ptr<MemMap>>* opened_dex_files_map,
     /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
   TimingLogger::ScopedTiming split("OpenDexFiles", timings_);
 
@@ -3691,6 +3716,44 @@
     return true;
   }
 
+  if (only_contains_uncompressed_zip_entries_) {
+    std::vector<std::unique_ptr<const DexFile>> dex_files;
+    std::vector<std::unique_ptr<MemMap>> maps;
+    for (OatDexFile& oat_dex_file : oat_dex_files_) {
+      std::string error_msg;
+      MemMap* map = oat_dex_file.source_.GetZipEntry()->MapDirectlyFromFile(
+          oat_dex_file.dex_file_location_data_, &error_msg);
+      if (map == nullptr) {
+        LOG(ERROR) << error_msg;
+        return false;
+      }
+      maps.emplace_back(map);
+      // Now, open the dex file.
+      const ArtDexFileLoader dex_file_loader;
+      dex_files.emplace_back(dex_file_loader.Open(map->Begin(),
+                                                  map->Size(),
+                                                  oat_dex_file.GetLocation(),
+                                                  oat_dex_file.dex_file_location_checksum_,
+                                                  /* oat_dex_file */ nullptr,
+                                                  verify,
+                                                  verify,
+                                                  &error_msg));
+      if (dex_files.back() == nullptr) {
+        LOG(ERROR) << "Failed to open dex file from oat file. File: " << oat_dex_file.GetLocation()
+                   << " Error: " << error_msg;
+        return false;
+      }
+      oat_dex_file.class_offsets_.resize(dex_files.back()->GetHeader().class_defs_size_);
+    }
+    *opened_dex_files_map = std::move(maps);
+    *opened_dex_files = std::move(dex_files);
+    CloseSources();
+    return true;
+  }
+  // We could have closed the sources at the point of writing the dex files, but to
+  // make it consistent with the case we're not writing the dex files, we close them now.
+  CloseSources();
+
   size_t map_offset = oat_dex_files_[0].dex_file_offset_;
   size_t length = vdex_size_ - map_offset;
 
@@ -3750,7 +3813,7 @@
     oat_dex_file.class_offsets_.resize(dex_files.back()->GetHeader().class_defs_size_);
   }
 
-  *opened_dex_files_map = std::move(dex_files_map);
+  opened_dex_files_map->push_back(std::move(dex_files_map));
   *opened_dex_files = std::move(dex_files);
   return true;
 }
diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h
index ac96bb8..c08c05a 100644
--- a/dex2oat/linker/oat_writer.h
+++ b/dex2oat/linker/oat_writer.h
@@ -174,7 +174,7 @@
                             SafeMap<std::string, std::string>* key_value_store,
                             bool verify,
                             bool update_input_vdex,
-                            /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
+                            /*out*/ std::vector<std::unique_ptr<MemMap>>* opened_dex_files_map,
                             /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files);
   bool WriteQuickeningInfo(OutputStream* vdex_out);
   bool WriteVerifierDeps(OutputStream* vdex_out, verifier::VerifierDeps* verifier_deps);
@@ -303,7 +303,7 @@
                     bool update_input_vdex);
   bool OpenDexFiles(File* file,
                     bool verify,
-                    /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
+                    /*out*/ std::vector<std::unique_ptr<MemMap>>* opened_dex_files_map,
                     /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files);
 
   size_t InitOatHeader(InstructionSet instruction_set,
@@ -370,6 +370,8 @@
   const CompilerDriver* compiler_driver_;
   ImageWriter* image_writer_;
   const bool compiling_boot_image_;
+  // Whether the dex files being compiled are all uncompressed in the APK.
+  bool only_contains_uncompressed_zip_entries_;
 
   // note OatFile does not take ownership of the DexFiles
   const std::vector<const DexFile*>* dex_files_;
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index 4e5fd72..cd6ca51 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -188,7 +188,7 @@
         oat_file);
     elf_writer->Start();
     OutputStream* oat_rodata = elf_writer->StartRoData();
-    std::unique_ptr<MemMap> opened_dex_files_map;
+    std::vector<std::unique_ptr<MemMap>> opened_dex_files_maps;
     std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
     if (!oat_writer.WriteAndOpenDexFiles(vdex_file,
                                          oat_rodata,
@@ -197,7 +197,7 @@
                                          &key_value_store,
                                          verify,
                                          /* update_input_vdex */ false,
-                                         &opened_dex_files_map,
+                                         &opened_dex_files_maps,
                                          &opened_dex_files)) {
       return false;
     }
@@ -257,7 +257,9 @@
       return false;
     }
 
-    opened_dex_files_maps_.emplace_back(std::move(opened_dex_files_map));
+    for (std::unique_ptr<MemMap>& map : opened_dex_files_maps) {
+      opened_dex_files_maps_.emplace_back(std::move(map));
+    }
     for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files) {
       opened_dex_files_.emplace_back(dex_file.release());
     }
diff --git a/dexlayout/compact_dex_writer.cc b/dexlayout/compact_dex_writer.cc
index dd1eee7..2f601b6 100644
--- a/dexlayout/compact_dex_writer.cc
+++ b/dexlayout/compact_dex_writer.cc
@@ -24,20 +24,20 @@
 
 namespace art {
 
-CompactDexWriter::CompactDexWriter(dex_ir::Header* header,
-                                   MemMap* mem_map,
-                                   DexLayout* dex_layout,
-                                   CompactDexLevel compact_dex_level)
-    : DexWriter(header, mem_map, dex_layout, /*compute_offsets*/ true),
-      compact_dex_level_(compact_dex_level),
-      data_dedupe_(/*bucket_count*/ 32,
-                   HashedMemoryRange::HashEqual(mem_map->Begin()),
-                   HashedMemoryRange::HashEqual(mem_map->Begin())) {
-  CHECK(compact_dex_level_ != CompactDexLevel::kCompactDexLevelNone);
+CompactDexWriter::CompactDexWriter(DexLayout* dex_layout)
+    : DexWriter(dex_layout, /*compute_offsets*/ true) {
+  CHECK(GetCompactDexLevel() != CompactDexLevel::kCompactDexLevelNone);
 }
 
-uint32_t CompactDexWriter::WriteDebugInfoOffsetTable(uint32_t offset) {
-  const uint32_t start_offset = offset;
+CompactDexLevel CompactDexWriter::GetCompactDexLevel() const {
+  return dex_layout_->GetOptions().compact_dex_level_;
+}
+
+CompactDexWriter::Container::Container(bool dedupe_code_items)
+    : code_item_dedupe_(dedupe_code_items, &data_section_) {}
+
+uint32_t CompactDexWriter::WriteDebugInfoOffsetTable(Stream* stream) {
+  const uint32_t start_offset = stream->Tell();
   const dex_ir::Collections& collections = header_->GetCollections();
   // Debug offsets for method indexes. 0 means no debug info.
   std::vector<uint32_t> debug_info_offsets(collections.MethodIdsSize(), 0u);
@@ -79,15 +79,16 @@
                                         &debug_info_base_,
                                         &debug_info_offsets_table_offset_);
   // Align the table and write it out.
-  offset = RoundUp(offset, CompactDexDebugInfoOffsetTable::kAlignment);
-  debug_info_offsets_pos_ = offset;
-  offset += Write(data.data(), data.size(), offset);
+  stream->AlignTo(CompactDexDebugInfoOffsetTable::kAlignment);
+  debug_info_offsets_pos_ = stream->Tell();
+  stream->Write(data.data(), data.size());
 
   // Verify that the whole table decodes as expected and measure average performance.
   const bool kMeasureAndTestOutput = dex_layout_->GetOptions().verify_output_;
   if (kMeasureAndTestOutput && !debug_info_offsets.empty()) {
     uint64_t start_time = NanoTime();
-    CompactDexDebugInfoOffsetTable::Accessor accessor(mem_map_->Begin() + debug_info_offsets_pos_,
+    stream->Begin();
+    CompactDexDebugInfoOffsetTable::Accessor accessor(stream->Begin() + debug_info_offsets_pos_,
                                                       debug_info_base_,
                                                       debug_info_offsets_table_offset_);
 
@@ -99,19 +100,19 @@
               << (end_time - start_time) / debug_info_offsets.size();
   }
 
-  return offset - start_offset;
+  return stream->Tell() - start_offset;
 }
 
-uint32_t CompactDexWriter::WriteCodeItem(dex_ir::CodeItem* code_item,
-                                         uint32_t offset,
+uint32_t CompactDexWriter::WriteCodeItem(Stream* stream,
+                                         dex_ir::CodeItem* code_item,
                                          bool reserve_only) {
   DCHECK(code_item != nullptr);
   DCHECK(!reserve_only) << "Not supported because of deduping.";
-  const uint32_t start_offset = offset;
+  const uint32_t start_offset = stream->Tell();
 
   // Align to minimum requirements, additional alignment requirements are handled below after we
   // know the preheader size.
-  offset = RoundUp(offset, CompactDexFile::CodeItem::kAlignment);
+  stream->AlignTo(CompactDexFile::CodeItem::kAlignment);
 
   CompactDexFile::CodeItem disk_code_item;
 
@@ -127,7 +128,7 @@
   const size_t preheader_bytes = (preheader_end - preheader) * sizeof(preheader[0]);
 
   static constexpr size_t kPayloadInstructionRequiredAlignment = 4;
-  const uint32_t current_code_item_start = offset + preheader_bytes;
+  const uint32_t current_code_item_start = stream->Tell() + preheader_bytes;
   if (!IsAlignedParam(current_code_item_start, kPayloadInstructionRequiredAlignment)) {
     // If the preheader is going to make the code unaligned, consider adding 2 bytes of padding
     // before if required.
@@ -137,49 +138,60 @@
       if (opcode == Instruction::FILL_ARRAY_DATA ||
           opcode == Instruction::PACKED_SWITCH ||
           opcode == Instruction::SPARSE_SWITCH) {
-        offset += RoundUp(current_code_item_start, kPayloadInstructionRequiredAlignment) -
-            current_code_item_start;
+        stream->Skip(
+            RoundUp(current_code_item_start, kPayloadInstructionRequiredAlignment) -
+                current_code_item_start);
         break;
       }
     }
   }
 
-  const uint32_t data_start = offset;
+  const uint32_t data_start = stream->Tell();
 
   // Write preheader first.
-  offset += Write(reinterpret_cast<const uint8_t*>(preheader), preheader_bytes, offset);
+  stream->Write(reinterpret_cast<const uint8_t*>(preheader), preheader_bytes);
   // Registered offset is after the preheader.
-  ProcessOffset(&offset, code_item);
+  ProcessOffset(stream, code_item);
   // Avoid using sizeof so that we don't write the fake instruction array at the end of the code
   // item.
-  offset += Write(&disk_code_item,
-                  OFFSETOF_MEMBER(CompactDexFile::CodeItem, insns_),
-                  offset);
+  stream->Write(&disk_code_item, OFFSETOF_MEMBER(CompactDexFile::CodeItem, insns_));
   // Write the instructions.
-  offset += Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t), offset);
+  stream->Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t));
   // Write the post instruction data.
-  offset += WriteCodeItemPostInstructionData(code_item, offset, reserve_only);
+  WriteCodeItemPostInstructionData(stream, code_item, reserve_only);
 
-  if (dex_layout_->GetOptions().dedupe_code_items_ && compute_offsets_) {
-    // After having written, try to dedupe the whole code item (excluding padding).
-    uint32_t deduped_offset = DedupeData(data_start, offset, code_item->GetOffset());
-    if (deduped_offset != kDidNotDedupe) {
+  if (compute_offsets_) {
+    // After having written, maybe dedupe the whole code item (excluding padding).
+    const uint32_t deduped_offset = code_item_dedupe_->Dedupe(data_start,
+                                                              stream->Tell(),
+                                                              code_item->GetOffset());
+    if (deduped_offset != Deduper::kDidNotDedupe) {
       code_item->SetOffset(deduped_offset);
       // Undo the offset for all that we wrote since we deduped.
-      offset = start_offset;
+      stream->Seek(start_offset);
     }
   }
 
-  return offset - start_offset;
+  return stream->Tell() - start_offset;
 }
 
-uint32_t CompactDexWriter::DedupeData(uint32_t data_start,
-                                      uint32_t data_end,
-                                      uint32_t item_offset) {
+
+CompactDexWriter::Deduper::Deduper(bool enabled, DexContainer::Section* section)
+    : enabled_(enabled),
+      dedupe_map_(/*bucket_count*/ 32,
+                  HashedMemoryRange::HashEqual(section),
+                  HashedMemoryRange::HashEqual(section)) {}
+
+uint32_t CompactDexWriter::Deduper::Dedupe(uint32_t data_start,
+                                           uint32_t data_end,
+                                           uint32_t item_offset) {
+  if (!enabled_) {
+    return kDidNotDedupe;
+  }
   HashedMemoryRange range {data_start, data_end - data_start};
-  auto existing = data_dedupe_.emplace(range, item_offset);
+  auto existing = dedupe_map_.emplace(range, item_offset);
   if (!existing.second) {
-    // Failed to insert, item already existed in the map.
+    // Failed to insert means we deduped, return the existing item offset.
     return existing.first->second;
   }
   return kDidNotDedupe;
@@ -223,7 +235,7 @@
   });
 }
 
-void CompactDexWriter::WriteHeader() {
+void CompactDexWriter::WriteHeader(Stream* stream) {
   CompactDexFile::Header header;
   CompactDexFile::WriteMagic(&header.magic_[0]);
   CompactDexFile::WriteCurrentVersion(&header.magic_[0]);
@@ -263,78 +275,99 @@
   if (header_->SupportDefaultMethods()) {
     header.feature_flags_ |= static_cast<uint32_t>(CompactDexFile::FeatureFlags::kDefaultMethods);
   }
-  UNUSED(Write(reinterpret_cast<uint8_t*>(&header), sizeof(header), 0u));
+  stream->Seek(0);
+  stream->Overwrite(reinterpret_cast<uint8_t*>(&header), sizeof(header));
 }
 
 size_t CompactDexWriter::GetHeaderSize() const {
   return sizeof(CompactDexFile::Header);
 }
 
-void CompactDexWriter::WriteMemMap() {
+void CompactDexWriter::Write(DexContainer* output)  {
+  CHECK(output->IsCompactDexContainer());
+  Container* const container = down_cast<Container*>(output);
+  // For now, use the same stream for both data and metadata.
+  Stream stream(output->GetMainSection());
+  Stream* main_stream = &stream;
+  Stream* data_stream = &stream;
+  code_item_dedupe_ = &container->code_item_dedupe_;
+
   // Starting offset is right after the header.
-  uint32_t offset = GetHeaderSize();
+  main_stream->Seek(GetHeaderSize());
 
   dex_ir::Collections& collection = header_->GetCollections();
 
   // Based on: https://source.android.com/devices/tech/dalvik/dex-format
   // Since the offsets may not be calculated already, the writing must be done in the correct order.
-  const uint32_t string_ids_offset = offset;
-  offset += WriteStringIds(offset, /*reserve_only*/ true);
-  offset += WriteTypeIds(offset);
-  const uint32_t proto_ids_offset = offset;
-  offset += WriteProtoIds(offset, /*reserve_only*/ true);
-  offset += WriteFieldIds(offset);
-  offset += WriteMethodIds(offset);
-  const uint32_t class_defs_offset = offset;
-  offset += WriteClassDefs(offset, /*reserve_only*/ true);
-  const uint32_t call_site_ids_offset = offset;
-  offset += WriteCallSiteIds(offset, /*reserve_only*/ true);
-  offset += WriteMethodHandles(offset);
+  const uint32_t string_ids_offset = main_stream->Tell();
+  WriteStringIds(main_stream, /*reserve_only*/ true);
+  WriteTypeIds(main_stream);
+  const uint32_t proto_ids_offset = main_stream->Tell();
+  WriteProtoIds(main_stream, /*reserve_only*/ true);
+  WriteFieldIds(main_stream);
+  WriteMethodIds(main_stream);
+  const uint32_t class_defs_offset = main_stream->Tell();
+  WriteClassDefs(main_stream, /*reserve_only*/ true);
+  const uint32_t call_site_ids_offset = main_stream->Tell();
+  WriteCallSiteIds(main_stream, /*reserve_only*/ true);
+  WriteMethodHandles(main_stream);
 
   uint32_t data_offset_ = 0u;
   if (compute_offsets_) {
     // Data section.
-    offset = RoundUp(offset, kDataSectionAlignment);
-    data_offset_ = offset;
+    data_stream->AlignTo(kDataSectionAlignment);
+    data_offset_ = data_stream->Tell();
   }
 
   // Write code item first to minimize the space required for encoded methods.
   // For cdex, the code items don't depend on the debug info.
-  offset += WriteCodeItems(offset, /*reserve_only*/ false);
+  WriteCodeItems(data_stream, /*reserve_only*/ false);
 
   // Sort the debug infos by method index order, this reduces size by ~0.1% by reducing the size of
   // the debug info offset table.
   SortDebugInfosByMethodIndex();
-  offset += WriteDebugInfoItems(offset);
+  WriteDebugInfoItems(data_stream);
 
-  offset += WriteEncodedArrays(offset);
-  offset += WriteAnnotations(offset);
-  offset += WriteAnnotationSets(offset);
-  offset += WriteAnnotationSetRefs(offset);
-  offset += WriteAnnotationsDirectories(offset);
-  offset += WriteTypeLists(offset);
-  offset += WriteClassDatas(offset);
-  offset += WriteStringDatas(offset);
+  WriteEncodedArrays(data_stream);
+  WriteAnnotations(data_stream);
+  WriteAnnotationSets(data_stream);
+  WriteAnnotationSetRefs(data_stream);
+  WriteAnnotationsDirectories(data_stream);
+  WriteTypeLists(data_stream);
+  WriteClassDatas(data_stream);
+  WriteStringDatas(data_stream);
 
   // Write delayed id sections that depend on data sections.
-  WriteStringIds(string_ids_offset, /*reserve_only*/ false);
-  WriteProtoIds(proto_ids_offset, /*reserve_only*/ false);
-  WriteClassDefs(class_defs_offset, /*reserve_only*/ false);
-  WriteCallSiteIds(call_site_ids_offset, /*reserve_only*/ false);
+  {
+    Stream::ScopedSeek seek(main_stream, string_ids_offset);
+    WriteStringIds(main_stream, /*reserve_only*/ false);
+  }
+  {
+    Stream::ScopedSeek seek(main_stream, proto_ids_offset);
+    WriteProtoIds(main_stream, /*reserve_only*/ false);
+  }
+  {
+    Stream::ScopedSeek seek(main_stream, class_defs_offset);
+    WriteClassDefs(main_stream, /*reserve_only*/ false);
+  }
+  {
+    Stream::ScopedSeek seek(main_stream, call_site_ids_offset);
+    WriteCallSiteIds(main_stream, /*reserve_only*/ false);
+  }
 
   // Write the map list.
   if (compute_offsets_) {
-    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeMapList));
-    collection.SetMapListOffset(offset);
+    data_stream->AlignTo(SectionAlignment(DexFile::kDexTypeMapList));
+    collection.SetMapListOffset(data_stream->Tell());
   } else {
-    offset = collection.MapListOffset();
+    data_stream->Seek(collection.MapListOffset());
   }
-  offset += GenerateAndWriteMapItems(offset);
-  offset = RoundUp(offset, kDataSectionAlignment);
+  GenerateAndWriteMapItems(data_stream);
+  data_stream->AlignTo(kDataSectionAlignment);
 
   // Map items are included in the data section.
   if (compute_offsets_) {
-    header_->SetDataSize(offset - data_offset_);
+    header_->SetDataSize(data_stream->Tell() - data_offset_);
     if (header_->DataSize() != 0) {
       // Offset must be zero when the size is zero.
       header_->SetDataOffset(data_offset_);
@@ -348,25 +381,34 @@
   if (link_data.size() > 0) {
     CHECK_EQ(header_->LinkSize(), static_cast<uint32_t>(link_data.size()));
     if (compute_offsets_) {
-      header_->SetLinkOffset(offset);
+      header_->SetLinkOffset(data_stream->Tell());
+    } else {
+      data_stream->Seek(header_->LinkOffset());
     }
-    offset += Write(&link_data[0], link_data.size(), header_->LinkOffset());
+    data_stream->Write(&link_data[0], link_data.size());
   }
 
   // Write debug info offset table last to make dex file verifier happy.
-  offset += WriteDebugInfoOffsetTable(offset);
+  WriteDebugInfoOffsetTable(data_stream);
 
   // Write header last.
   if (compute_offsets_) {
-    header_->SetFileSize(offset);
+    header_->SetFileSize(main_stream->Tell());
   }
-  WriteHeader();
+  WriteHeader(main_stream);
 
   if (dex_layout_->GetOptions().update_checksum_) {
-    header_->SetChecksum(DexFile::CalculateChecksum(mem_map_->Begin(), offset));
+    header_->SetChecksum(DexFile::CalculateChecksum(main_stream->Begin(), header_->FileSize()));
     // Rewrite the header with the calculated checksum.
-    WriteHeader();
+    WriteHeader(main_stream);
   }
+  // Trim the map to make it sized as large as the dex file.
+  output->GetMainSection()->Resize(header_->FileSize());
+}
+
+std::unique_ptr<DexContainer> CompactDexWriter::CreateDexContainer() const {
+  return std::unique_ptr<DexContainer>(
+      new CompactDexWriter::Container(dex_layout_->GetOptions().dedupe_code_items_));
 }
 
 }  // namespace art
diff --git a/dexlayout/compact_dex_writer.h b/dexlayout/compact_dex_writer.h
index cb53cae..626b85a 100644
--- a/dexlayout/compact_dex_writer.h
+++ b/dexlayout/compact_dex_writer.h
@@ -19,6 +19,7 @@
 #ifndef ART_DEXLAYOUT_COMPACT_DEX_WRITER_H_
 #define ART_DEXLAYOUT_COMPACT_DEX_WRITER_H_
 
+#include <memory>  // For unique_ptr
 #include <unordered_map>
 
 #include "dex_writer.h"
@@ -26,62 +27,108 @@
 
 namespace art {
 
-class HashedMemoryRange {
+// Compact dex writer for a single dex.
+class CompactDexWriter : public DexWriter {
  public:
-  uint32_t offset_;
-  uint32_t length_;
+  explicit CompactDexWriter(DexLayout* dex_layout);
 
-  class HashEqual {
+ protected:
+  class Deduper {
    public:
-    explicit HashEqual(const uint8_t* data) : data_(data) {}
+    static const uint32_t kDidNotDedupe = 0;
 
-    // Equal function.
-    bool operator()(const HashedMemoryRange& a, const HashedMemoryRange& b) const {
-      return a.length_ == b.length_ && std::equal(data_ + a.offset_,
-                                                  data_ + a.offset_ + a.length_,
-                                                  data_ + b.offset_);
+    // if not enabled, Dedupe will always return kDidNotDedupe.
+    explicit Deduper(bool enabled, DexContainer::Section* section);
+
+    // Deduplicate a blob of data that has been written to mem_map.
+    // Returns the offset of the deduplicated data or kDidNotDedupe did deduplication did not occur.
+    uint32_t Dedupe(uint32_t data_start, uint32_t data_end, uint32_t item_offset);
+
+   private:
+    class HashedMemoryRange {
+     public:
+      uint32_t offset_;
+      uint32_t length_;
+
+      class HashEqual {
+       public:
+        explicit HashEqual(DexContainer::Section* section) : section_(section) {}
+
+        // Equal function.
+        bool operator()(const HashedMemoryRange& a, const HashedMemoryRange& b) const {
+          if (a.length_ != b.length_) {
+            return false;
+          }
+          const uint8_t* data = Data();
+          return std::equal(data + a.offset_, data + a.offset_ + a.length_, data + b.offset_);
+        }
+
+        // Hash function.
+        size_t operator()(const HashedMemoryRange& range) const {
+          return HashBytes(Data() + range.offset_, range.length_);
+        }
+
+        ALWAYS_INLINE uint8_t* Data() const {
+          return section_->Begin();
+        }
+
+       private:
+        DexContainer::Section* const section_;
+      };
+    };
+
+    const bool enabled_;
+
+    // Dedupe map.
+    std::unordered_map<HashedMemoryRange,
+                       uint32_t,
+                       HashedMemoryRange::HashEqual,
+                       HashedMemoryRange::HashEqual> dedupe_map_;
+  };
+
+ public:
+  class Container : public DexContainer {
+   public:
+    Section* GetMainSection() OVERRIDE {
+      return &main_section_;
     }
 
-    // Hash function.
-    size_t operator()(const HashedMemoryRange& range) const {
-      return HashBytes(data_ + range.offset_, range.length_);
+    Section* GetDataSection() OVERRIDE {
+      return &data_section_;
+    }
+
+    bool IsCompactDexContainer() const OVERRIDE {
+      return true;
     }
 
    private:
-    const uint8_t* data_;
-  };
-};
+    explicit Container(bool dedupe_code_items);
 
-class CompactDexWriter : public DexWriter {
- public:
-  CompactDexWriter(dex_ir::Header* header,
-                   MemMap* mem_map,
-                   DexLayout* dex_layout,
-                   CompactDexLevel compact_dex_level);
+    VectorSection main_section_;
+    VectorSection data_section_;
+    Deduper code_item_dedupe_;
+
+    friend class CompactDexWriter;
+  };
 
  protected:
-  void WriteMemMap() OVERRIDE;
+  void Write(DexContainer* output) OVERRIDE;
 
-  void WriteHeader() OVERRIDE;
+  std::unique_ptr<DexContainer> CreateDexContainer() const OVERRIDE;
+
+  void WriteHeader(Stream* stream) OVERRIDE;
 
   size_t GetHeaderSize() const OVERRIDE;
 
-  uint32_t WriteDebugInfoOffsetTable(uint32_t offset);
+  uint32_t WriteDebugInfoOffsetTable(Stream* stream);
 
-  uint32_t WriteCodeItem(dex_ir::CodeItem* code_item, uint32_t offset, bool reserve_only) OVERRIDE;
+  uint32_t WriteCodeItem(Stream* stream, dex_ir::CodeItem* code_item, bool reserve_only) OVERRIDE;
 
   void SortDebugInfosByMethodIndex();
 
-  // Deduplicate a blob of data that has been written to mem_map. The backing storage is the actual
-  // mem_map contents to reduce RAM usage.
-  // Returns the offset of the deduplicated data or 0 if kDidNotDedupe did not occur.
-  uint32_t DedupeData(uint32_t data_start, uint32_t data_end, uint32_t item_offset);
+  CompactDexLevel GetCompactDexLevel() const;
 
  private:
-  const CompactDexLevel compact_dex_level_;
-
-  static const uint32_t kDidNotDedupe = 0;
-
   // Position in the compact dex file for the debug info table data starts.
   uint32_t debug_info_offsets_pos_ = 0u;
 
@@ -91,11 +138,8 @@
   // Base offset of where debug info starts in the dex file.
   uint32_t debug_info_base_ = 0u;
 
-  // Dedupe map.
-  std::unordered_map<HashedMemoryRange,
-                     uint32_t,
-                     HashedMemoryRange::HashEqual,
-                     HashedMemoryRange::HashEqual> data_dedupe_;
+  // State for where we are deduping.
+  Deduper* code_item_dedupe_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(CompactDexWriter);
 };
diff --git a/dexlayout/dex_container.h b/dexlayout/dex_container.h
new file mode 100644
index 0000000..7c426cb
--- /dev/null
+++ b/dexlayout/dex_container.h
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ *
+ * Header file of an in-memory representation of DEX files.
+ */
+
+#ifndef ART_DEXLAYOUT_DEX_CONTAINER_H_
+#define ART_DEXLAYOUT_DEX_CONTAINER_H_
+
+#include <vector>
+
+namespace art {
+
+// Dex container holds the artifacts produced by dexlayout and contains up to two sections: a main
+// section and a data section.
+// This container may also hold metadata used for multi dex deduplication in the future.
+class DexContainer {
+ public:
+  virtual ~DexContainer() {}
+
+  class Section {
+   public:
+    virtual ~Section() {}
+
+    // Returns the start of the memory region.
+    virtual uint8_t* Begin() = 0;
+
+    // Size in bytes.
+    virtual size_t Size() const = 0;
+
+    // Resize the backing storage.
+    virtual void Resize(size_t size) = 0;
+
+    // Returns the end of the memory region.
+    uint8_t* End() {
+      return Begin() + Size();
+    }
+  };
+
+  // Vector backed section.
+  class VectorSection : public Section {
+   public:
+    virtual ~VectorSection() {}
+
+    uint8_t* Begin() OVERRIDE {
+      return &data_[0];
+    }
+
+    size_t Size() const OVERRIDE {
+      return data_.size();
+    }
+
+    void Resize(size_t size) OVERRIDE {
+      data_.resize(size, 0u);
+    }
+
+   private:
+    std::vector<uint8_t> data_;
+  };
+
+  virtual Section* GetMainSection() = 0;
+  virtual Section* GetDataSection() = 0;
+  virtual bool IsCompactDexContainer() const = 0;
+};
+
+}  // namespace art
+
+#endif  // ART_DEXLAYOUT_DEX_CONTAINER_H_
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index 3627717..1a84d23 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -486,11 +486,11 @@
   virtual ~Item() { }
 
   // Return the assigned offset.
-  uint32_t GetOffset() const {
+  uint32_t GetOffset() const WARN_UNUSED {
     CHECK(OffsetAssigned());
     return offset_;
   }
-  uint32_t GetSize() const { return size_; }
+  uint32_t GetSize() const WARN_UNUSED { return size_; }
   void SetOffset(uint32_t offset) { offset_ = offset; }
   void SetSize(uint32_t size) { size_ = size; }
   bool OffsetAssigned() const {
diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc
index d26c948..eb038a0 100644
--- a/dexlayout/dex_writer.cc
+++ b/dexlayout/dex_writer.cc
@@ -30,7 +30,7 @@
 
 namespace art {
 
-size_t EncodeIntValue(int32_t value, uint8_t* buffer) {
+static size_t EncodeIntValue(int32_t value, uint8_t* buffer) {
   size_t length = 0;
   if (value >= 0) {
     while (value > 0x7f) {
@@ -47,7 +47,7 @@
   return length;
 }
 
-size_t EncodeUIntValue(uint32_t value, uint8_t* buffer) {
+static size_t EncodeUIntValue(uint32_t value, uint8_t* buffer) {
   size_t length = 0;
   do {
     buffer[length++] = static_cast<uint8_t>(value);
@@ -56,7 +56,7 @@
   return length;
 }
 
-size_t EncodeLongValue(int64_t value, uint8_t* buffer) {
+static size_t EncodeLongValue(int64_t value, uint8_t* buffer) {
   size_t length = 0;
   if (value >= 0) {
     while (value > 0x7f) {
@@ -78,7 +78,7 @@
   uint32_t i_;
 };
 
-size_t EncodeFloatValue(float value, uint8_t* buffer) {
+static size_t EncodeFloatValue(float value, uint8_t* buffer) {
   FloatUnion float_union;
   float_union.f_ = value;
   uint32_t int_value = float_union.i_;
@@ -95,7 +95,7 @@
   uint64_t l_;
 };
 
-size_t EncodeDoubleValue(double value, uint8_t* buffer) {
+static size_t EncodeDoubleValue(double value, uint8_t* buffer) {
   DoubleUnion double_union;
   double_union.d_ = value;
   uint64_t long_value = double_union.l_;
@@ -107,26 +107,13 @@
   return 7 - index;
 }
 
-size_t DexWriter::Write(const void* buffer, size_t length, size_t offset) {
-  DCHECK_LE(offset + length, mem_map_->Size());
-  memcpy(mem_map_->Begin() + offset, buffer, length);
-  return length;
-}
+DexWriter::DexWriter(DexLayout* dex_layout, bool compute_offsets)
+    : header_(dex_layout->GetHeader()),
+      dex_layout_(dex_layout),
+      compute_offsets_(compute_offsets) {}
 
-size_t DexWriter::WriteSleb128(uint32_t value, size_t offset) {
-  uint8_t buffer[8];
-  EncodeSignedLeb128(buffer, value);
-  return Write(buffer, SignedLeb128Size(value), offset);
-}
-
-size_t DexWriter::WriteUleb128(uint32_t value, size_t offset) {
-  uint8_t buffer[8];
-  EncodeUnsignedLeb128(buffer, value);
-  return Write(buffer, UnsignedLeb128Size(value), offset);
-}
-
-size_t DexWriter::WriteEncodedValue(dex_ir::EncodedValue* encoded_value, size_t offset) {
-  size_t original_offset = offset;
+size_t DexWriter::WriteEncodedValue(Stream* stream, dex_ir::EncodedValue* encoded_value) {
+  size_t original_offset = stream->Tell();
   size_t start = 0;
   size_t length;
   uint8_t buffer[8];
@@ -175,284 +162,285 @@
       length = EncodeUIntValue(encoded_value->GetMethodId()->GetIndex(), buffer);
       break;
     case DexFile::kDexAnnotationArray:
-      offset += WriteEncodedValueHeader(type, 0, offset);
-      offset += WriteEncodedArray(encoded_value->GetEncodedArray()->GetEncodedValues(), offset);
-      return offset - original_offset;
+      WriteEncodedValueHeader(stream, type, 0);
+      WriteEncodedArray(stream, encoded_value->GetEncodedArray()->GetEncodedValues());
+      return stream->Tell() - original_offset;
     case DexFile::kDexAnnotationAnnotation:
-      offset += WriteEncodedValueHeader(type, 0, offset);
-      offset += WriteEncodedAnnotation(encoded_value->GetEncodedAnnotation(), offset);
-      return offset - original_offset;
+      WriteEncodedValueHeader(stream, type, 0);
+      WriteEncodedAnnotation(stream, encoded_value->GetEncodedAnnotation());
+      return stream->Tell() - original_offset;
     case DexFile::kDexAnnotationNull:
-      return WriteEncodedValueHeader(type, 0, offset);
+      return WriteEncodedValueHeader(stream, type, 0);
     case DexFile::kDexAnnotationBoolean:
-      return WriteEncodedValueHeader(type, encoded_value->GetBoolean() ? 1 : 0, offset);
+      return WriteEncodedValueHeader(stream, type, encoded_value->GetBoolean() ? 1 : 0);
     default:
       return 0;
   }
-  offset += WriteEncodedValueHeader(type, length - 1, offset);
-  offset += Write(buffer + start, length, offset);
-  return offset - original_offset;
+  WriteEncodedValueHeader(stream, type, length - 1);
+  stream->Write(buffer + start, length);
+  return stream->Tell() - original_offset;
 }
 
-size_t DexWriter::WriteEncodedValueHeader(int8_t value_type, size_t value_arg, size_t offset) {
+size_t DexWriter::WriteEncodedValueHeader(Stream* stream, int8_t value_type, size_t value_arg) {
   uint8_t buffer[1] = { static_cast<uint8_t>((value_arg << 5) | value_type) };
-  return Write(buffer, sizeof(uint8_t), offset);
+  return stream->Write(buffer, sizeof(uint8_t));
 }
 
-size_t DexWriter::WriteEncodedArray(dex_ir::EncodedValueVector* values, size_t offset) {
-  size_t original_offset = offset;
-  offset += WriteUleb128(values->size(), offset);
+size_t DexWriter::WriteEncodedArray(Stream* stream, dex_ir::EncodedValueVector* values) {
+  size_t original_offset = stream->Tell();
+  stream->WriteUleb128(values->size());
   for (std::unique_ptr<dex_ir::EncodedValue>& value : *values) {
-    offset += WriteEncodedValue(value.get(), offset);
+    WriteEncodedValue(stream, value.get());
   }
-  return offset - original_offset;
+  return stream->Tell() - original_offset;
 }
 
-size_t DexWriter::WriteEncodedAnnotation(dex_ir::EncodedAnnotation* annotation, size_t offset) {
-  size_t original_offset = offset;
-  offset += WriteUleb128(annotation->GetType()->GetIndex(), offset);
-  offset += WriteUleb128(annotation->GetAnnotationElements()->size(), offset);
+size_t DexWriter::WriteEncodedAnnotation(Stream* stream, dex_ir::EncodedAnnotation* annotation) {
+  size_t original_offset = stream->Tell();
+  stream->WriteUleb128(annotation->GetType()->GetIndex());
+  stream->WriteUleb128(annotation->GetAnnotationElements()->size());
   for (std::unique_ptr<dex_ir::AnnotationElement>& annotation_element :
       *annotation->GetAnnotationElements()) {
-    offset += WriteUleb128(annotation_element->GetName()->GetIndex(), offset);
-    offset += WriteEncodedValue(annotation_element->GetValue(), offset);
+    stream->WriteUleb128(annotation_element->GetName()->GetIndex());
+    WriteEncodedValue(stream, annotation_element->GetValue());
   }
-  return offset - original_offset;
+  return stream->Tell() - original_offset;
 }
 
-size_t DexWriter::WriteEncodedFields(dex_ir::FieldItemVector* fields, size_t offset) {
-  size_t original_offset = offset;
+size_t DexWriter::WriteEncodedFields(Stream* stream, dex_ir::FieldItemVector* fields) {
+  size_t original_offset = stream->Tell();
   uint32_t prev_index = 0;
   for (std::unique_ptr<dex_ir::FieldItem>& field : *fields) {
     uint32_t index = field->GetFieldId()->GetIndex();
-    offset += WriteUleb128(index - prev_index, offset);
-    offset += WriteUleb128(field->GetAccessFlags(), offset);
+    stream->WriteUleb128(index - prev_index);
+    stream->WriteUleb128(field->GetAccessFlags());
     prev_index = index;
   }
-  return offset - original_offset;
+  return stream->Tell() - original_offset;
 }
 
-size_t DexWriter::WriteEncodedMethods(dex_ir::MethodItemVector* methods, size_t offset) {
-  size_t original_offset = offset;
+size_t DexWriter::WriteEncodedMethods(Stream* stream, dex_ir::MethodItemVector* methods) {
+  size_t original_offset = stream->Tell();
   uint32_t prev_index = 0;
   for (std::unique_ptr<dex_ir::MethodItem>& method : *methods) {
     uint32_t index = method->GetMethodId()->GetIndex();
     uint32_t code_off = method->GetCodeItem() == nullptr ? 0 : method->GetCodeItem()->GetOffset();
-    offset += WriteUleb128(index - prev_index, offset);
-    offset += WriteUleb128(method->GetAccessFlags(), offset);
-    offset += WriteUleb128(code_off, offset);
+    stream->WriteUleb128(index - prev_index);
+    stream->WriteUleb128(method->GetAccessFlags());
+    stream->WriteUleb128(code_off);
     prev_index = index;
   }
-  return offset - original_offset;
+  return stream->Tell() - original_offset;
 }
 
 // TODO: Refactor this to remove duplicated boiler plate. One way to do this is adding
 // function that takes a CollectionVector<T> and uses overloading.
-uint32_t DexWriter::WriteStringIds(uint32_t offset, bool reserve_only) {
-  const uint32_t start = offset;
+uint32_t DexWriter::WriteStringIds(Stream* stream, bool reserve_only) {
+  const uint32_t start = stream->Tell();
   for (std::unique_ptr<dex_ir::StringId>& string_id : header_->GetCollections().StringIds()) {
-    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeStringIdItem));
+    stream->AlignTo(SectionAlignment(DexFile::kDexTypeStringIdItem));
     if (reserve_only) {
-      offset += string_id->GetSize();
+      stream->Skip(string_id->GetSize());
     } else {
       uint32_t string_data_off = string_id->DataItem()->GetOffset();
-      offset += Write(&string_data_off, string_id->GetSize(), offset);
+      stream->Write(&string_data_off, string_id->GetSize());
     }
   }
-  if (compute_offsets_ && start != offset) {
+  if (compute_offsets_ && start != stream->Tell()) {
     header_->GetCollections().SetStringIdsOffset(start);
   }
-  return offset - start;
+  return stream->Tell() - start;
 }
 
-uint32_t DexWriter::WriteStringDatas(uint32_t offset) {
-  const uint32_t start = offset;
+uint32_t DexWriter::WriteStringDatas(Stream* stream) {
+  const uint32_t start = stream->Tell();
   for (std::unique_ptr<dex_ir::StringData>& string_data : header_->GetCollections().StringDatas()) {
-    ProcessOffset(&offset, string_data.get());
-    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeStringDataItem));
-    offset += WriteUleb128(CountModifiedUtf8Chars(string_data->Data()), offset);
+    ProcessOffset(stream, string_data.get());
+    stream->AlignTo(SectionAlignment(DexFile::kDexTypeStringDataItem));
+    stream->WriteUleb128(CountModifiedUtf8Chars(string_data->Data()));
+    stream->Write(string_data->Data(), strlen(string_data->Data()));
     // Skip null terminator (already zeroed out, no need to write).
-    offset += Write(string_data->Data(), strlen(string_data->Data()), offset) + 1u;
+    stream->Skip(1);
   }
-  if (compute_offsets_ && start != offset) {
+  if (compute_offsets_ && start != stream->Tell()) {
     header_->GetCollections().SetStringDatasOffset(start);
   }
-  return offset - start;
+  return stream->Tell() - start;
 }
 
-uint32_t DexWriter::WriteTypeIds(uint32_t offset) {
+uint32_t DexWriter::WriteTypeIds(Stream* stream) {
   uint32_t descriptor_idx[1];
-  const uint32_t start = offset;
+  const uint32_t start = stream->Tell();
   for (std::unique_ptr<dex_ir::TypeId>& type_id : header_->GetCollections().TypeIds()) {
-    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeTypeIdItem));
-    ProcessOffset(&offset, type_id.get());
+    stream->AlignTo(SectionAlignment(DexFile::kDexTypeTypeIdItem));
+    ProcessOffset(stream, type_id.get());
     descriptor_idx[0] = type_id->GetStringId()->GetIndex();
-    offset += Write(descriptor_idx, type_id->GetSize(), offset);
+    stream->Write(descriptor_idx, type_id->GetSize());
   }
-  if (compute_offsets_ && start != offset) {
+  if (compute_offsets_ && start != stream->Tell()) {
     header_->GetCollections().SetTypeIdsOffset(start);
   }
-  return offset - start;
+  return stream->Tell() - start;
 }
 
-uint32_t DexWriter::WriteTypeLists(uint32_t offset) {
+uint32_t DexWriter::WriteTypeLists(Stream* stream) {
   uint32_t size[1];
   uint16_t list[1];
-  const uint32_t start = offset;
+  const uint32_t start = stream->Tell();
   for (std::unique_ptr<dex_ir::TypeList>& type_list : header_->GetCollections().TypeLists()) {
-    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeTypeList));
+    stream->AlignTo(SectionAlignment(DexFile::kDexTypeTypeList));
     size[0] = type_list->GetTypeList()->size();
-    ProcessOffset(&offset, type_list.get());
-    offset += Write(size, sizeof(uint32_t), offset);
+    ProcessOffset(stream, type_list.get());
+    stream->Write(size, sizeof(uint32_t));
     for (const dex_ir::TypeId* type_id : *type_list->GetTypeList()) {
       list[0] = type_id->GetIndex();
-      offset += Write(list, sizeof(uint16_t), offset);
+      stream->Write(list, sizeof(uint16_t));
     }
   }
-  if (compute_offsets_ && start != offset) {
+  if (compute_offsets_ && start != stream->Tell()) {
     header_->GetCollections().SetTypeListsOffset(start);
   }
-  return offset - start;
+  return stream->Tell() - start;
 }
 
-uint32_t DexWriter::WriteProtoIds(uint32_t offset, bool reserve_only) {
+uint32_t DexWriter::WriteProtoIds(Stream* stream, bool reserve_only) {
   uint32_t buffer[3];
-  const uint32_t start = offset;
+  const uint32_t start = stream->Tell();
   for (std::unique_ptr<dex_ir::ProtoId>& proto_id : header_->GetCollections().ProtoIds()) {
-    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeProtoIdItem));
-    ProcessOffset(&offset, proto_id.get());
+    stream->AlignTo(SectionAlignment(DexFile::kDexTypeProtoIdItem));
+    ProcessOffset(stream, proto_id.get());
     if (reserve_only) {
-      offset += proto_id->GetSize();
+      stream->Skip(proto_id->GetSize());
     } else {
       buffer[0] = proto_id->Shorty()->GetIndex();
       buffer[1] = proto_id->ReturnType()->GetIndex();
       buffer[2] = proto_id->Parameters() == nullptr ? 0 : proto_id->Parameters()->GetOffset();
-      offset += Write(buffer, proto_id->GetSize(), offset);
+      stream->Write(buffer, proto_id->GetSize());
     }
   }
-  if (compute_offsets_ && start != offset) {
+  if (compute_offsets_ && start != stream->Tell()) {
     header_->GetCollections().SetProtoIdsOffset(start);
   }
-  return offset - start;
+  return stream->Tell() - start;
 }
 
-uint32_t DexWriter::WriteFieldIds(uint32_t offset) {
+uint32_t DexWriter::WriteFieldIds(Stream* stream) {
   uint16_t buffer[4];
-  const uint32_t start = offset;
+  const uint32_t start = stream->Tell();
   for (std::unique_ptr<dex_ir::FieldId>& field_id : header_->GetCollections().FieldIds()) {
-    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeFieldIdItem));
-    ProcessOffset(&offset, field_id.get());
+    stream->AlignTo(SectionAlignment(DexFile::kDexTypeFieldIdItem));
+    ProcessOffset(stream, field_id.get());
     buffer[0] = field_id->Class()->GetIndex();
     buffer[1] = field_id->Type()->GetIndex();
     buffer[2] = field_id->Name()->GetIndex();
     buffer[3] = field_id->Name()->GetIndex() >> 16;
-    offset += Write(buffer, field_id->GetSize(), offset);
+    stream->Write(buffer, field_id->GetSize());
   }
-  if (compute_offsets_ && start != offset) {
+  if (compute_offsets_ && start != stream->Tell()) {
     header_->GetCollections().SetFieldIdsOffset(start);
   }
-  return offset - start;
+  return stream->Tell() - start;
 }
 
-uint32_t DexWriter::WriteMethodIds(uint32_t offset) {
+uint32_t DexWriter::WriteMethodIds(Stream* stream) {
   uint16_t buffer[4];
-  const uint32_t start = offset;
+  const uint32_t start = stream->Tell();
   for (std::unique_ptr<dex_ir::MethodId>& method_id : header_->GetCollections().MethodIds()) {
-    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeMethodIdItem));
-    ProcessOffset(&offset, method_id.get());
+    stream->AlignTo(SectionAlignment(DexFile::kDexTypeMethodIdItem));
+    ProcessOffset(stream, method_id.get());
     buffer[0] = method_id->Class()->GetIndex();
     buffer[1] = method_id->Proto()->GetIndex();
     buffer[2] = method_id->Name()->GetIndex();
     buffer[3] = method_id->Name()->GetIndex() >> 16;
-    offset += Write(buffer, method_id->GetSize(), offset);
+    stream->Write(buffer, method_id->GetSize());
   }
-  if (compute_offsets_ && start != offset) {
+  if (compute_offsets_ && start != stream->Tell()) {
     header_->GetCollections().SetMethodIdsOffset(start);
   }
-  return offset - start;
+  return stream->Tell() - start;
 }
 
-uint32_t DexWriter::WriteEncodedArrays(uint32_t offset) {
-  const uint32_t start = offset;
+uint32_t DexWriter::WriteEncodedArrays(Stream* stream) {
+  const uint32_t start = stream->Tell();
   for (std::unique_ptr<dex_ir::EncodedArrayItem>& encoded_array :
       header_->GetCollections().EncodedArrayItems()) {
-    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeEncodedArrayItem));
-    ProcessOffset(&offset, encoded_array.get());
-    offset += WriteEncodedArray(encoded_array->GetEncodedValues(), offset);
+    stream->AlignTo(SectionAlignment(DexFile::kDexTypeEncodedArrayItem));
+    ProcessOffset(stream, encoded_array.get());
+    WriteEncodedArray(stream, encoded_array->GetEncodedValues());
   }
-  if (compute_offsets_ && start != offset) {
+  if (compute_offsets_ && start != stream->Tell()) {
     header_->GetCollections().SetEncodedArrayItemsOffset(start);
   }
-  return offset - start;
+  return stream->Tell() - start;
 }
 
-uint32_t DexWriter::WriteAnnotations(uint32_t offset) {
+uint32_t DexWriter::WriteAnnotations(Stream* stream) {
   uint8_t visibility[1];
-  const uint32_t start = offset;
+  const uint32_t start = stream->Tell();
   for (std::unique_ptr<dex_ir::AnnotationItem>& annotation :
       header_->GetCollections().AnnotationItems()) {
-    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeAnnotationItem));
+    stream->AlignTo(SectionAlignment(DexFile::kDexTypeAnnotationItem));
     visibility[0] = annotation->GetVisibility();
-    ProcessOffset(&offset, annotation.get());
-    offset += Write(visibility, sizeof(uint8_t), offset);
-    offset += WriteEncodedAnnotation(annotation->GetAnnotation(), offset);
+    ProcessOffset(stream, annotation.get());
+    stream->Write(visibility, sizeof(uint8_t));
+    WriteEncodedAnnotation(stream, annotation->GetAnnotation());
   }
-  if (compute_offsets_ && start != offset) {
+  if (compute_offsets_ && start != stream->Tell()) {
     header_->GetCollections().SetAnnotationItemsOffset(start);
   }
-  return offset - start;
+  return stream->Tell() - start;
 }
 
-uint32_t DexWriter::WriteAnnotationSets(uint32_t offset) {
+uint32_t DexWriter::WriteAnnotationSets(Stream* stream) {
   uint32_t size[1];
   uint32_t annotation_off[1];
-  const uint32_t start = offset;
+  const uint32_t start = stream->Tell();
   for (std::unique_ptr<dex_ir::AnnotationSetItem>& annotation_set :
       header_->GetCollections().AnnotationSetItems()) {
-    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeAnnotationSetItem));
+    stream->AlignTo(SectionAlignment(DexFile::kDexTypeAnnotationSetItem));
     size[0] = annotation_set->GetItems()->size();
-    ProcessOffset(&offset, annotation_set.get());
-    offset += Write(size, sizeof(uint32_t), offset);
+    ProcessOffset(stream, annotation_set.get());
+    stream->Write(size, sizeof(uint32_t));
     for (dex_ir::AnnotationItem* annotation : *annotation_set->GetItems()) {
       annotation_off[0] = annotation->GetOffset();
-      offset += Write(annotation_off, sizeof(uint32_t), offset);
+      stream->Write(annotation_off, sizeof(uint32_t));
     }
   }
-  if (compute_offsets_ && start != offset) {
+  if (compute_offsets_ && start != stream->Tell()) {
     header_->GetCollections().SetAnnotationSetItemsOffset(start);
   }
-  return offset - start;
+  return stream->Tell() - start;
 }
 
-uint32_t DexWriter::WriteAnnotationSetRefs(uint32_t offset) {
+uint32_t DexWriter::WriteAnnotationSetRefs(Stream* stream) {
   uint32_t size[1];
   uint32_t annotations_off[1];
-  const uint32_t start = offset;
+  const uint32_t start = stream->Tell();
   for (std::unique_ptr<dex_ir::AnnotationSetRefList>& annotation_set_ref :
       header_->GetCollections().AnnotationSetRefLists()) {
-    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeAnnotationSetRefList));
+    stream->AlignTo(SectionAlignment(DexFile::kDexTypeAnnotationSetRefList));
     size[0] = annotation_set_ref->GetItems()->size();
-    ProcessOffset(&offset, annotation_set_ref.get());
-    offset += Write(size, sizeof(uint32_t), offset);
+    ProcessOffset(stream, annotation_set_ref.get());
+    stream->Write(size, sizeof(uint32_t));
     for (dex_ir::AnnotationSetItem* annotation_set : *annotation_set_ref->GetItems()) {
       annotations_off[0] = annotation_set == nullptr ? 0 : annotation_set->GetOffset();
-      offset += Write(annotations_off, sizeof(uint32_t), offset);
+      stream->Write(annotations_off, sizeof(uint32_t));
     }
   }
-  if (compute_offsets_ && start != offset) {
+  if (compute_offsets_ && start != stream->Tell()) {
     header_->GetCollections().SetAnnotationSetRefListsOffset(start);
   }
-  return offset - start;
+  return stream->Tell() - start;
 }
 
-uint32_t DexWriter::WriteAnnotationsDirectories(uint32_t offset) {
+uint32_t DexWriter::WriteAnnotationsDirectories(Stream* stream) {
   uint32_t directory_buffer[4];
   uint32_t annotation_buffer[2];
-  const uint32_t start = offset;
+  const uint32_t start = stream->Tell();
   for (std::unique_ptr<dex_ir::AnnotationsDirectoryItem>& annotations_directory :
       header_->GetCollections().AnnotationsDirectoryItems()) {
-    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeAnnotationsDirectoryItem));
-    ProcessOffset(&offset, annotations_directory.get());
+    stream->AlignTo(SectionAlignment(DexFile::kDexTypeAnnotationsDirectoryItem));
+    ProcessOffset(stream, annotations_directory.get());
     directory_buffer[0] = annotations_directory->GetClassAnnotation() == nullptr ? 0 :
         annotations_directory->GetClassAnnotation()->GetOffset();
     directory_buffer[1] = annotations_directory->GetFieldAnnotations() == nullptr ? 0 :
@@ -461,13 +449,13 @@
         annotations_directory->GetMethodAnnotations()->size();
     directory_buffer[3] = annotations_directory->GetParameterAnnotations() == nullptr ? 0 :
         annotations_directory->GetParameterAnnotations()->size();
-    offset += Write(directory_buffer, 4 * sizeof(uint32_t), offset);
+    stream->Write(directory_buffer, 4 * sizeof(uint32_t));
     if (annotations_directory->GetFieldAnnotations() != nullptr) {
       for (std::unique_ptr<dex_ir::FieldAnnotation>& field :
           *annotations_directory->GetFieldAnnotations()) {
         annotation_buffer[0] = field->GetFieldId()->GetIndex();
         annotation_buffer[1] = field->GetAnnotationSetItem()->GetOffset();
-        offset += Write(annotation_buffer, 2 * sizeof(uint32_t), offset);
+        stream->Write(annotation_buffer, 2 * sizeof(uint32_t));
       }
     }
     if (annotations_directory->GetMethodAnnotations() != nullptr) {
@@ -475,7 +463,7 @@
           *annotations_directory->GetMethodAnnotations()) {
         annotation_buffer[0] = method->GetMethodId()->GetIndex();
         annotation_buffer[1] = method->GetAnnotationSetItem()->GetOffset();
-        offset += Write(annotation_buffer, 2 * sizeof(uint32_t), offset);
+        stream->Write(annotation_buffer, 2 * sizeof(uint32_t));
       }
     }
     if (annotations_directory->GetParameterAnnotations() != nullptr) {
@@ -483,37 +471,36 @@
           *annotations_directory->GetParameterAnnotations()) {
         annotation_buffer[0] = parameter->GetMethodId()->GetIndex();
         annotation_buffer[1] = parameter->GetAnnotations()->GetOffset();
-        offset += Write(annotation_buffer, 2 * sizeof(uint32_t), offset);
+        stream->Write(annotation_buffer, 2 * sizeof(uint32_t));
       }
     }
   }
-  if (compute_offsets_ && start != offset) {
+  if (compute_offsets_ && start != stream->Tell()) {
     header_->GetCollections().SetAnnotationsDirectoryItemsOffset(start);
   }
-  return offset - start;
+  return stream->Tell() - start;
 }
 
-uint32_t DexWriter::WriteDebugInfoItems(uint32_t offset) {
-  const uint32_t start = offset;
+uint32_t DexWriter::WriteDebugInfoItems(Stream* stream) {
+  const uint32_t start = stream->Tell();
   for (std::unique_ptr<dex_ir::DebugInfoItem>& debug_info :
       header_->GetCollections().DebugInfoItems()) {
-    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeDebugInfoItem));
-    ProcessOffset(&offset, debug_info.get());
-    offset += Write(debug_info->GetDebugInfo(), debug_info->GetDebugInfoSize(), offset);
+    stream->AlignTo(SectionAlignment(DexFile::kDexTypeDebugInfoItem));
+    ProcessOffset(stream, debug_info.get());
+    stream->Write(debug_info->GetDebugInfo(), debug_info->GetDebugInfoSize());
   }
-  if (compute_offsets_ && start != offset) {
+  if (compute_offsets_ && start != stream->Tell()) {
     header_->GetCollections().SetDebugInfoItemsOffset(start);
   }
-  return offset - start;
+  return stream->Tell() - start;
 }
 
-uint32_t DexWriter::WriteCodeItemPostInstructionData(dex_ir::CodeItem* code_item,
-                                                     uint32_t offset,
+uint32_t DexWriter::WriteCodeItemPostInstructionData(Stream* stream,
+                                                     dex_ir::CodeItem* code_item,
                                                      bool reserve_only) {
-  const uint32_t start_offset = offset;
+  const uint32_t start_offset = stream->Tell();
   if (code_item->TriesSize() != 0) {
-    // Align for the try items.
-    offset = RoundUp(offset, DexFile::TryItem::kAlignment);
+    stream->AlignTo(DexFile::TryItem::kAlignment);
     // Write try items.
     for (std::unique_ptr<const dex_ir::TryItem>& try_item : *code_item->Tries()) {
       DexFile::TryItem disk_try_item;
@@ -522,38 +509,37 @@
         disk_try_item.insn_count_ = try_item->InsnCount();
         disk_try_item.handler_off_ = try_item->GetHandlers()->GetListOffset();
       }
-      offset += Write(&disk_try_item, sizeof(disk_try_item), offset);
+      stream->Write(&disk_try_item, sizeof(disk_try_item));
     }
-    size_t max_offset = offset;
     // Leave offset pointing to the end of the try items.
-    UNUSED(WriteUleb128(code_item->Handlers()->size(), offset));
+    const size_t offset = stream->Tell();
+    size_t max_offset = offset + stream->WriteUleb128(code_item->Handlers()->size());
     for (std::unique_ptr<const dex_ir::CatchHandler>& handlers : *code_item->Handlers()) {
-      size_t list_offset = offset + handlers->GetListOffset();
+      stream->Seek(offset + handlers->GetListOffset());
       uint32_t size = handlers->HasCatchAll() ? (handlers->GetHandlers()->size() - 1) * -1 :
           handlers->GetHandlers()->size();
-      list_offset += WriteSleb128(size, list_offset);
+      stream->WriteSleb128(size);
       for (std::unique_ptr<const dex_ir::TypeAddrPair>& handler : *handlers->GetHandlers()) {
         if (handler->GetTypeId() != nullptr) {
-          list_offset += WriteUleb128(handler->GetTypeId()->GetIndex(), list_offset);
+          stream->WriteUleb128(handler->GetTypeId()->GetIndex());
         }
-        list_offset += WriteUleb128(handler->GetAddress(), list_offset);
+        stream->WriteUleb128(handler->GetAddress());
       }
       // TODO: Clean this up to write the handlers in address order.
-      max_offset = std::max(max_offset, list_offset);
+      max_offset = std::max(max_offset, stream->Tell());
     }
-    offset = max_offset;
+    stream->Seek(max_offset);
   }
-
-  return offset - start_offset;
+  return stream->Tell() - start_offset;
 }
 
-uint32_t DexWriter::WriteCodeItem(dex_ir::CodeItem* code_item,
-                                  uint32_t offset,
+uint32_t DexWriter::WriteCodeItem(Stream* stream,
+                                  dex_ir::CodeItem* code_item,
                                   bool reserve_only) {
   DCHECK(code_item != nullptr);
-  const uint32_t start_offset = offset;
-  offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeCodeItem));
-  ProcessOffset(&offset, code_item);
+  const uint32_t start_offset = stream->Tell();
+  stream->AlignTo(SectionAlignment(DexFile::kDexTypeCodeItem));
+  ProcessOffset(stream, code_item);
 
   StandardDexFile::CodeItem disk_code_item;
   if (!reserve_only) {
@@ -568,50 +554,50 @@
   }
   // Avoid using sizeof so that we don't write the fake instruction array at the end of the code
   // item.
-  offset += Write(&disk_code_item,
-                  OFFSETOF_MEMBER(StandardDexFile::CodeItem, insns_),
-                  offset);
+  stream->Write(&disk_code_item, OFFSETOF_MEMBER(StandardDexFile::CodeItem, insns_));
   // Write the instructions.
-  offset += Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t), offset);
+  stream->Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t));
   // Write the post instruction data.
-  offset += WriteCodeItemPostInstructionData(code_item, offset, reserve_only);
-  return offset - start_offset;
+  WriteCodeItemPostInstructionData(stream, code_item, reserve_only);
+  if (reserve_only) {
+    stream->Clear(start_offset, stream->Tell() - start_offset);
+  }
+  return stream->Tell() - start_offset;
 }
 
-uint32_t DexWriter::WriteCodeItems(uint32_t offset, bool reserve_only) {
+uint32_t DexWriter::WriteCodeItems(Stream* stream, bool reserve_only) {
   DexLayoutSection* code_section = nullptr;
   if (!reserve_only && dex_layout_ != nullptr) {
     code_section = &dex_layout_->GetSections().sections_[static_cast<size_t>(
         DexLayoutSections::SectionType::kSectionTypeCode)];
   }
-  uint32_t start = offset;
+  const uint32_t start = stream->Tell();
   for (auto& code_item : header_->GetCollections().CodeItems()) {
-    const size_t code_item_size = WriteCodeItem(code_item.get(), offset, reserve_only);
+    const size_t code_item_size = WriteCodeItem(stream, code_item.get(), reserve_only);
     // Only add the section hotness info once.
     if (!reserve_only && code_section != nullptr) {
       auto it = dex_layout_->LayoutHotnessInfo().code_item_layout_.find(code_item.get());
       if (it != dex_layout_->LayoutHotnessInfo().code_item_layout_.end()) {
         code_section->parts_[static_cast<size_t>(it->second)].CombineSection(
-            offset,
-            offset + code_item_size);
+            stream->Tell() - code_item_size,
+            stream->Tell());
       }
     }
-    offset += code_item_size;
   }
 
-  if (compute_offsets_ && start != offset) {
+  if (compute_offsets_ && start != stream->Tell()) {
     header_->GetCollections().SetCodeItemsOffset(start);
   }
-  return offset - start;
+  return stream->Tell() - start;
 }
 
-uint32_t DexWriter::WriteClassDefs(uint32_t offset, bool reserve_only) {
-  const uint32_t start = offset;
+uint32_t DexWriter::WriteClassDefs(Stream* stream, bool reserve_only) {
+  const uint32_t start = stream->Tell();
   uint32_t class_def_buffer[8];
   for (std::unique_ptr<dex_ir::ClassDef>& class_def : header_->GetCollections().ClassDefs()) {
-    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeClassDefItem));
+    stream->AlignTo(SectionAlignment(DexFile::kDexTypeClassDefItem));
     if (reserve_only) {
-      offset += class_def->GetSize();
+      stream->Skip(class_def->GetSize());
     } else {
       class_def_buffer[0] = class_def->ClassType()->GetIndex();
       class_def_buffer[1] = class_def->GetAccessFlags();
@@ -626,94 +612,94 @@
           class_def->GetClassData()->GetOffset();
       class_def_buffer[7] = class_def->StaticValues() == nullptr ? 0 :
           class_def->StaticValues()->GetOffset();
-      offset += Write(class_def_buffer, class_def->GetSize(), offset);
+      stream->Write(class_def_buffer, class_def->GetSize());
     }
   }
-  if (compute_offsets_ && start != offset) {
+  if (compute_offsets_ && start != stream->Tell()) {
     header_->GetCollections().SetClassDefsOffset(start);
   }
-  return offset - start;
+  return stream->Tell() - start;
 }
 
-uint32_t DexWriter::WriteClassDatas(uint32_t offset) {
-  const uint32_t start = offset;
+uint32_t DexWriter::WriteClassDatas(Stream* stream) {
+  const uint32_t start = stream->Tell();
   for (const std::unique_ptr<dex_ir::ClassData>& class_data :
       header_->GetCollections().ClassDatas()) {
-    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeClassDataItem));
-    ProcessOffset(&offset, class_data.get());
-    offset += WriteUleb128(class_data->StaticFields()->size(), offset);
-    offset += WriteUleb128(class_data->InstanceFields()->size(), offset);
-    offset += WriteUleb128(class_data->DirectMethods()->size(), offset);
-    offset += WriteUleb128(class_data->VirtualMethods()->size(), offset);
-    offset += WriteEncodedFields(class_data->StaticFields(), offset);
-    offset += WriteEncodedFields(class_data->InstanceFields(), offset);
-    offset += WriteEncodedMethods(class_data->DirectMethods(), offset);
-    offset += WriteEncodedMethods(class_data->VirtualMethods(), offset);
+    stream->AlignTo(SectionAlignment(DexFile::kDexTypeClassDataItem));
+    ProcessOffset(stream, class_data.get());
+    stream->WriteUleb128(class_data->StaticFields()->size());
+    stream->WriteUleb128(class_data->InstanceFields()->size());
+    stream->WriteUleb128(class_data->DirectMethods()->size());
+    stream->WriteUleb128(class_data->VirtualMethods()->size());
+    WriteEncodedFields(stream, class_data->StaticFields());
+    WriteEncodedFields(stream, class_data->InstanceFields());
+    WriteEncodedMethods(stream, class_data->DirectMethods());
+    WriteEncodedMethods(stream, class_data->VirtualMethods());
   }
-  if (compute_offsets_ && start != offset) {
+  if (compute_offsets_ && start != stream->Tell()) {
     header_->GetCollections().SetClassDatasOffset(start);
   }
-  return offset - start;
+  return stream->Tell() - start;
 }
 
-uint32_t DexWriter::WriteCallSiteIds(uint32_t offset, bool reserve_only) {
-  const uint32_t start = offset;
+uint32_t DexWriter::WriteCallSiteIds(Stream* stream, bool reserve_only) {
+  const uint32_t start = stream->Tell();
   uint32_t call_site_off[1];
   for (std::unique_ptr<dex_ir::CallSiteId>& call_site_id :
       header_->GetCollections().CallSiteIds()) {
-    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeCallSiteIdItem));
+    stream->AlignTo(SectionAlignment(DexFile::kDexTypeCallSiteIdItem));
     if (reserve_only) {
-      offset += call_site_id->GetSize();
+      stream->Skip(call_site_id->GetSize());
     } else {
       call_site_off[0] = call_site_id->CallSiteItem()->GetOffset();
-      offset += Write(call_site_off, call_site_id->GetSize(), offset);
+      stream->Write(call_site_off, call_site_id->GetSize());
     }
   }
-  if (compute_offsets_ && start != offset) {
+  if (compute_offsets_ && start != stream->Tell()) {
     header_->GetCollections().SetCallSiteIdsOffset(start);
   }
-  return offset - start;
+  return stream->Tell() - start;
 }
 
-uint32_t DexWriter::WriteMethodHandles(uint32_t offset) {
-  const uint32_t start = offset;
+uint32_t DexWriter::WriteMethodHandles(Stream* stream) {
+  const uint32_t start = stream->Tell();
   uint16_t method_handle_buff[4];
   for (std::unique_ptr<dex_ir::MethodHandleItem>& method_handle :
       header_->GetCollections().MethodHandleItems()) {
-    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeMethodHandleItem));
+    stream->AlignTo(SectionAlignment(DexFile::kDexTypeMethodHandleItem));
     method_handle_buff[0] = static_cast<uint16_t>(method_handle->GetMethodHandleType());
     method_handle_buff[1] = 0;  // unused.
     method_handle_buff[2] = method_handle->GetFieldOrMethodId()->GetIndex();
     method_handle_buff[3] = 0;  // unused.
-    offset += Write(method_handle_buff, method_handle->GetSize(), offset);
+    stream->Write(method_handle_buff, method_handle->GetSize());
   }
-  if (compute_offsets_ && start != offset) {
+  if (compute_offsets_ && start != stream->Tell()) {
     header_->GetCollections().SetMethodHandleItemsOffset(start);
   }
-  return offset - start;
+  return stream->Tell() - start;
 }
 
-uint32_t DexWriter::WriteMapItems(uint32_t offset, MapItemQueue* queue) {
+uint32_t DexWriter::WriteMapItems(Stream* stream, MapItemQueue* queue) {
   // All the sections should already have been added.
   uint16_t uint16_buffer[2];
   uint32_t uint32_buffer[2];
   uint16_buffer[1] = 0;
   uint32_buffer[0] = queue->size();
-  const uint32_t start = offset;
-  offset += Write(uint32_buffer, sizeof(uint32_t), offset);
+  const uint32_t start = stream->Tell();
+  stream->Write(uint32_buffer, sizeof(uint32_t));
   while (!queue->empty()) {
     const MapItem& map_item = queue->top();
     uint16_buffer[0] = map_item.type_;
     uint32_buffer[0] = map_item.size_;
     uint32_buffer[1] = map_item.offset_;
-    offset += Write(uint16_buffer, 2 * sizeof(uint16_t), offset);
-    offset += Write(uint32_buffer, 2 * sizeof(uint32_t), offset);
+    stream->Write(uint16_buffer, 2 * sizeof(uint16_t));
+    stream->Write(uint32_buffer, 2 * sizeof(uint32_t));
     queue->pop();
   }
-  return offset - start;
+  return stream->Tell() - start;
 }
 
-uint32_t DexWriter::GenerateAndWriteMapItems(uint32_t offset) {
+uint32_t DexWriter::GenerateAndWriteMapItems(Stream* stream) {
   dex_ir::Collections& collection = header_->GetCollections();
   MapItemQueue queue;
 
@@ -777,10 +763,10 @@
                               collection.AnnotationsDirectoryItemsOffset()));
 
   // Write the map items.
-  return WriteMapItems(offset, &queue);
+  return WriteMapItems(stream, &queue);
 }
 
-void DexWriter::WriteHeader() {
+void DexWriter::WriteHeader(Stream* stream) {
   StandardDexFile::Header header;
   if (CompactDexFile::IsMagicValid(header_->Magic())) {
     StandardDexFile::WriteMagic(header.magic_);
@@ -818,78 +804,97 @@
 
   CHECK_EQ(sizeof(header), GetHeaderSize());
   static_assert(sizeof(header) == 0x70, "Size doesn't match dex spec");
-  UNUSED(Write(reinterpret_cast<uint8_t*>(&header), sizeof(header), 0u));
+  stream->Seek(0);
+  stream->Overwrite(reinterpret_cast<uint8_t*>(&header), sizeof(header));
 }
 
 size_t DexWriter::GetHeaderSize() const {
   return sizeof(StandardDexFile::Header);
 }
 
-void DexWriter::WriteMemMap() {
+void DexWriter::Write(DexContainer* output) {
+  Stream stream_storage(output->GetMainSection());
+  Stream* stream = &stream_storage;
+
   // Starting offset is right after the header.
-  uint32_t offset = GetHeaderSize();
+  stream->Seek(GetHeaderSize());
 
   dex_ir::Collections& collection = header_->GetCollections();
 
   // Based on: https://source.android.com/devices/tech/dalvik/dex-format
   // Since the offsets may not be calculated already, the writing must be done in the correct order.
-  const uint32_t string_ids_offset = offset;
-  offset += WriteStringIds(offset, /*reserve_only*/ true);
-  offset += WriteTypeIds(offset);
-  const uint32_t proto_ids_offset = offset;
-  offset += WriteProtoIds(offset, /*reserve_only*/ true);
-  offset += WriteFieldIds(offset);
-  offset += WriteMethodIds(offset);
-  const uint32_t class_defs_offset = offset;
-  offset += WriteClassDefs(offset, /*reserve_only*/ true);
-  const uint32_t call_site_ids_offset = offset;
-  offset += WriteCallSiteIds(offset, /*reserve_only*/ true);
-  offset += WriteMethodHandles(offset);
+  const uint32_t string_ids_offset = stream->Tell();
+  WriteStringIds(stream, /*reserve_only*/ true);
+  WriteTypeIds(stream);
+  const uint32_t proto_ids_offset = stream->Tell();
+  WriteProtoIds(stream, /*reserve_only*/ true);
+  WriteFieldIds(stream);
+  WriteMethodIds(stream);
+  const uint32_t class_defs_offset = stream->Tell();
+  WriteClassDefs(stream, /*reserve_only*/ true);
+  const uint32_t call_site_ids_offset = stream->Tell();
+  WriteCallSiteIds(stream, /*reserve_only*/ true);
+  WriteMethodHandles(stream);
 
   uint32_t data_offset_ = 0u;
   if (compute_offsets_) {
     // Data section.
-    offset = RoundUp(offset, kDataSectionAlignment);
-    data_offset_ = offset;
+    stream->AlignTo(kDataSectionAlignment);
+    data_offset_ = stream->Tell();
   }
 
   // Write code item first to minimize the space required for encoded methods.
   // Reserve code item space since we need the debug offsets to actually write them.
-  const uint32_t code_items_offset = offset;
-  offset += WriteCodeItems(offset, /*reserve_only*/ true);
+  const uint32_t code_items_offset = stream->Tell();
+  WriteCodeItems(stream, /*reserve_only*/ true);
   // Write debug info section.
-  offset += WriteDebugInfoItems(offset);
-  // Actually write code items since debug info offsets are calculated now.
-  WriteCodeItems(code_items_offset, /*reserve_only*/ false);
+  WriteDebugInfoItems(stream);
+  {
+    // Actually write code items since debug info offsets are calculated now.
+    Stream::ScopedSeek seek(stream, code_items_offset);
+    WriteCodeItems(stream, /*reserve_only*/ false);
+  }
 
-  offset += WriteEncodedArrays(offset);
-  offset += WriteAnnotations(offset);
-  offset += WriteAnnotationSets(offset);
-  offset += WriteAnnotationSetRefs(offset);
-  offset += WriteAnnotationsDirectories(offset);
-  offset += WriteTypeLists(offset);
-  offset += WriteClassDatas(offset);
-  offset += WriteStringDatas(offset);
+  WriteEncodedArrays(stream);
+  WriteAnnotations(stream);
+  WriteAnnotationSets(stream);
+  WriteAnnotationSetRefs(stream);
+  WriteAnnotationsDirectories(stream);
+  WriteTypeLists(stream);
+  WriteClassDatas(stream);
+  WriteStringDatas(stream);
 
   // Write delayed id sections that depend on data sections.
-  WriteStringIds(string_ids_offset, /*reserve_only*/ false);
-  WriteProtoIds(proto_ids_offset, /*reserve_only*/ false);
-  WriteClassDefs(class_defs_offset, /*reserve_only*/ false);
-  WriteCallSiteIds(call_site_ids_offset, /*reserve_only*/ false);
+  {
+    Stream::ScopedSeek seek(stream, string_ids_offset);
+    WriteStringIds(stream, /*reserve_only*/ false);
+  }
+  {
+    Stream::ScopedSeek seek(stream, proto_ids_offset);
+    WriteProtoIds(stream, /*reserve_only*/ false);
+  }
+  {
+    Stream::ScopedSeek seek(stream, class_defs_offset);
+    WriteClassDefs(stream, /*reserve_only*/ false);
+  }
+  {
+    Stream::ScopedSeek seek(stream, call_site_ids_offset);
+    WriteCallSiteIds(stream, /*reserve_only*/ false);
+  }
 
   // Write the map list.
   if (compute_offsets_) {
-    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeMapList));
-    collection.SetMapListOffset(offset);
+    stream->AlignTo(SectionAlignment(DexFile::kDexTypeMapList));
+    collection.SetMapListOffset(stream->Tell());
   } else {
-    offset = collection.MapListOffset();
+    stream->Seek(collection.MapListOffset());
   }
-  offset += GenerateAndWriteMapItems(offset);
-  offset = RoundUp(offset, kDataSectionAlignment);
+  GenerateAndWriteMapItems(stream);
+  stream->AlignTo(kDataSectionAlignment);
 
   // Map items are included in the data section.
   if (compute_offsets_) {
-    header_->SetDataSize(offset - data_offset_);
+    header_->SetDataSize(stream->Tell() - data_offset_);
     if (header_->DataSize() != 0) {
       // Offset must be zero when the size is zero.
       header_->SetDataOffset(data_offset_);
@@ -903,37 +908,45 @@
   if (link_data.size() > 0) {
     CHECK_EQ(header_->LinkSize(), static_cast<uint32_t>(link_data.size()));
     if (compute_offsets_) {
-      header_->SetLinkOffset(offset);
+      header_->SetLinkOffset(stream->Tell());
+    } else {
+      stream->Seek(header_->LinkOffset());
     }
-    offset += Write(&link_data[0], link_data.size(), header_->LinkOffset());
+    stream->Write(&link_data[0], link_data.size());
   }
 
   // Write header last.
   if (compute_offsets_) {
-    header_->SetFileSize(offset);
+    header_->SetFileSize(stream->Tell());
   }
-  WriteHeader();
+  WriteHeader(stream);
 
   if (dex_layout_->GetOptions().update_checksum_) {
-    header_->SetChecksum(DexFile::CalculateChecksum(mem_map_->Begin(), offset));
+    header_->SetChecksum(DexFile::CalculateChecksum(stream->Begin(), header_->FileSize()));
     // Rewrite the header with the calculated checksum.
-    WriteHeader();
+    WriteHeader(stream);
   }
+
+  // Trim the map to make it sized as large as the dex file.
+  output->GetMainSection()->Resize(header_->FileSize());
 }
 
-void DexWriter::Output(dex_ir::Header* header,
-                       MemMap* mem_map,
-                       DexLayout* dex_layout,
-                       bool compute_offsets,
-                       CompactDexLevel compact_dex_level) {
+void DexWriter::Output(DexLayout* dex_layout,
+                       std::unique_ptr<DexContainer>* container,
+                       bool compute_offsets) {
   CHECK(dex_layout != nullptr);
   std::unique_ptr<DexWriter> writer;
-  if (compact_dex_level != CompactDexLevel::kCompactDexLevelNone) {
-    writer.reset(new CompactDexWriter(header, mem_map, dex_layout, compact_dex_level));
+  if (dex_layout->GetOptions().compact_dex_level_ != CompactDexLevel::kCompactDexLevelNone) {
+    CHECK(compute_offsets) << "Compact dex requires computing offsets";
+    writer.reset(new CompactDexWriter(dex_layout));
   } else {
-    writer.reset(new DexWriter(header, mem_map, dex_layout, compute_offsets));
+    writer.reset(new DexWriter(dex_layout, compute_offsets));
   }
-  writer->WriteMemMap();
+  DCHECK(container != nullptr);
+  if (*container == nullptr) {
+    *container = writer->CreateDexContainer();
+  }
+  writer->Write(container->get());
 }
 
 void MapItemQueue::AddIfNotEmpty(const MapItem& item) {
@@ -942,4 +955,17 @@
   }
 }
 
+void DexWriter::ProcessOffset(Stream* stream, dex_ir::Item* item) {
+  if (compute_offsets_) {
+    item->SetOffset(stream->Tell());
+  } else {
+    // Not computing offsets, just use the one in the item.
+    stream->Seek(item->GetOffset());
+  }
+}
+
+std::unique_ptr<DexContainer> DexWriter::CreateDexContainer() const {
+  return std::unique_ptr<DexContainer>(new DexWriter::Container);
+}
+
 }  // namespace art
diff --git a/dexlayout/dex_writer.h b/dexlayout/dex_writer.h
index 892ea74..e581a8b 100644
--- a/dexlayout/dex_writer.h
+++ b/dexlayout/dex_writer.h
@@ -20,9 +20,11 @@
 #define ART_DEXLAYOUT_DEX_WRITER_H_
 
 #include <functional>
+#include <memory>  // For unique_ptr
 
 #include "base/unix_file/fd_file.h"
 #include "dex/compact_dex_level.h"
+#include "dex_container.h"
 #include "dex/dex_file.h"
 #include "dex_ir.h"
 #include "mem_map.h"
@@ -39,7 +41,7 @@
   // Not using DexFile::MapItemType since compact dex and standard dex file may have different
   // sections.
   MapItem() = default;
-  MapItem(uint32_t type, uint32_t size, uint32_t offset)
+  MapItem(uint32_t type, uint32_t size, size_t offset)
       : type_(type), size_(size), offset_(offset) { }
 
   // Sort by decreasing order since the priority_queue puts largest elements first.
@@ -63,6 +65,114 @@
   static constexpr uint32_t kDataSectionAlignment = sizeof(uint32_t) * 2;
   static constexpr uint32_t kDexSectionWordAlignment = 4;
 
+  // Stream that writes into a dex container section. Do not have two streams pointing to the same
+  // backing storage as there may be invalidation of backing storage to resize the section.
+  // Random access stream (consider refactoring).
+  class Stream {
+   public:
+    explicit Stream(DexContainer::Section* section) : section_(section) {
+      SyncWithSection();
+    }
+
+    const uint8_t* Begin() const {
+      return data_;
+    }
+
+    // Functions are not virtual (yet) for speed.
+    size_t Tell() const {
+      return position_;
+    }
+
+    void Seek(size_t position) {
+      position_ = position;
+    }
+
+    // Does not allow overwriting for bug prevention purposes.
+    ALWAYS_INLINE size_t Write(const void* buffer, size_t length) {
+      EnsureStorage(length);
+      for (size_t i = 0; i < length; ++i) {
+        DCHECK_EQ(data_[position_ + i], 0u);
+      }
+      memcpy(&data_[position_], buffer, length);
+      position_ += length;
+      return length;
+    }
+
+    ALWAYS_INLINE size_t Overwrite(const void* buffer, size_t length) {
+      EnsureStorage(length);
+      memcpy(&data_[position_], buffer, length);
+      position_ += length;
+      return length;
+    }
+
+    ALWAYS_INLINE size_t Clear(size_t position, size_t length) {
+      EnsureStorage(length);
+      memset(&data_[position], 0, length);
+      return length;
+    }
+
+    ALWAYS_INLINE size_t WriteSleb128(int32_t value) {
+      EnsureStorage(8);
+      uint8_t* ptr = &data_[position_];
+      const size_t len = EncodeSignedLeb128(ptr, value) - ptr;
+      position_ += len;
+      return len;
+    }
+
+    ALWAYS_INLINE size_t WriteUleb128(uint32_t value) {
+      EnsureStorage(8);
+      uint8_t* ptr = &data_[position_];
+      const size_t len = EncodeUnsignedLeb128(ptr, value) - ptr;
+      position_ += len;
+      return len;
+    }
+
+    ALWAYS_INLINE void AlignTo(const size_t alignment) {
+      position_ = RoundUp(position_, alignment);
+    }
+
+    ALWAYS_INLINE void Skip(const size_t count) {
+      position_ += count;
+    }
+
+    class ScopedSeek {
+     public:
+      ScopedSeek(Stream* stream, uint32_t offset) : stream_(stream), offset_(stream->Tell()) {
+        stream->Seek(offset);
+      }
+
+      ~ScopedSeek() {
+        stream_->Seek(offset_);
+      }
+
+     private:
+      Stream* const stream_;
+      const uint32_t offset_;
+    };
+
+   private:
+    ALWAYS_INLINE void EnsureStorage(size_t length) {
+      size_t end = position_ + length;
+      while (UNLIKELY(end > data_size_)) {
+        section_->Resize(data_size_ * 3 / 2 + 1);
+        SyncWithSection();
+      }
+    }
+
+    void SyncWithSection() {
+      data_ = section_->Begin();
+      data_size_ = section_->Size();
+    }
+
+    // Current position of the stream.
+    size_t position_ = 0u;
+    DexContainer::Section* const section_ = nullptr;
+    // Cached Begin() from the container to provide faster accesses.
+    uint8_t* data_ = nullptr;
+    // Cached Size from the container to provide faster accesses.
+    size_t data_size_ = 0u;
+  };
+
   static inline constexpr uint32_t SectionAlignment(DexFile::MapItemType type) {
     switch (type) {
       case DexFile::kDexTypeClassDataItem:
@@ -78,83 +188,85 @@
     }
   }
 
-  DexWriter(dex_ir::Header* header,
-            MemMap* mem_map,
-            DexLayout* dex_layout,
-            bool compute_offsets)
-      : header_(header),
-        mem_map_(mem_map),
-        dex_layout_(dex_layout),
-        compute_offsets_(compute_offsets) {}
+  class Container : public DexContainer {
+   public:
+    Section* GetMainSection() OVERRIDE {
+      return &main_section_;
+    }
 
-  static void Output(dex_ir::Header* header,
-                     MemMap* mem_map,
-                     DexLayout* dex_layout,
-                     bool compute_offsets,
-                     CompactDexLevel compact_dex_level);
+    Section* GetDataSection() OVERRIDE {
+      return &data_section_;
+    }
+
+    bool IsCompactDexContainer() const OVERRIDE {
+      return false;
+    }
+
+   private:
+    VectorSection main_section_;
+    VectorSection data_section_;
+
+    friend class CompactDexWriter;
+  };
+
+  DexWriter(DexLayout* dex_layout, bool compute_offsets);
+
+  static void Output(DexLayout* dex_layout,
+                     std::unique_ptr<DexContainer>* container,
+                     bool compute_offsets);
 
   virtual ~DexWriter() {}
 
  protected:
-  virtual void WriteMemMap();
+  virtual void Write(DexContainer* output);
+  virtual std::unique_ptr<DexContainer> CreateDexContainer() const;
 
-  size_t Write(const void* buffer, size_t length, size_t offset) WARN_UNUSED;
-  size_t WriteSleb128(uint32_t value, size_t offset) WARN_UNUSED;
-  size_t WriteUleb128(uint32_t value, size_t offset) WARN_UNUSED;
-  size_t WriteEncodedValue(dex_ir::EncodedValue* encoded_value, size_t offset) WARN_UNUSED;
-  size_t WriteEncodedValueHeader(int8_t value_type, size_t value_arg, size_t offset) WARN_UNUSED;
-  size_t WriteEncodedArray(dex_ir::EncodedValueVector* values, size_t offset) WARN_UNUSED;
-  size_t WriteEncodedAnnotation(dex_ir::EncodedAnnotation* annotation, size_t offset) WARN_UNUSED;
-  size_t WriteEncodedFields(dex_ir::FieldItemVector* fields, size_t offset) WARN_UNUSED;
-  size_t WriteEncodedMethods(dex_ir::MethodItemVector* methods, size_t offset) WARN_UNUSED;
+  size_t WriteEncodedValue(Stream* stream, dex_ir::EncodedValue* encoded_value);
+  size_t WriteEncodedValueHeader(Stream* stream, int8_t value_type, size_t value_arg);
+  size_t WriteEncodedArray(Stream* stream, dex_ir::EncodedValueVector* values);
+  size_t WriteEncodedAnnotation(Stream* stream, dex_ir::EncodedAnnotation* annotation);
+  size_t WriteEncodedFields(Stream* stream, dex_ir::FieldItemVector* fields);
+  size_t WriteEncodedMethods(Stream* stream, dex_ir::MethodItemVector* methods);
 
   // Header and id section
-  virtual void WriteHeader();
+  virtual void WriteHeader(Stream* stream);
   virtual size_t GetHeaderSize() const;
   // reserve_only means don't write, only reserve space. This is required since the string data
   // offsets must be assigned.
-  uint32_t WriteStringIds(uint32_t offset, bool reserve_only);
-  uint32_t WriteTypeIds(uint32_t offset);
-  uint32_t WriteProtoIds(uint32_t offset, bool reserve_only);
-  uint32_t WriteFieldIds(uint32_t offset);
-  uint32_t WriteMethodIds(uint32_t offset);
-  uint32_t WriteClassDefs(uint32_t offset, bool reserve_only);
-  uint32_t WriteCallSiteIds(uint32_t offset, bool reserve_only);
+  uint32_t WriteStringIds(Stream* stream, bool reserve_only);
+  uint32_t WriteTypeIds(Stream* stream);
+  uint32_t WriteProtoIds(Stream* stream, bool reserve_only);
+  uint32_t WriteFieldIds(Stream* stream);
+  uint32_t WriteMethodIds(Stream* stream);
+  uint32_t WriteClassDefs(Stream* stream, bool reserve_only);
+  uint32_t WriteCallSiteIds(Stream* stream, bool reserve_only);
 
-  uint32_t WriteEncodedArrays(uint32_t offset);
-  uint32_t WriteAnnotations(uint32_t offset);
-  uint32_t WriteAnnotationSets(uint32_t offset);
-  uint32_t WriteAnnotationSetRefs(uint32_t offset);
-  uint32_t WriteAnnotationsDirectories(uint32_t offset);
+  uint32_t WriteEncodedArrays(Stream* stream);
+  uint32_t WriteAnnotations(Stream* stream);
+  uint32_t WriteAnnotationSets(Stream* stream);
+  uint32_t WriteAnnotationSetRefs(Stream* stream);
+  uint32_t WriteAnnotationsDirectories(Stream* stream);
 
   // Data section.
-  uint32_t WriteDebugInfoItems(uint32_t offset);
-  uint32_t WriteCodeItems(uint32_t offset, bool reserve_only);
-  uint32_t WriteTypeLists(uint32_t offset);
-  uint32_t WriteStringDatas(uint32_t offset);
-  uint32_t WriteClassDatas(uint32_t offset);
-  uint32_t WriteMethodHandles(uint32_t offset);
-  uint32_t WriteMapItems(uint32_t offset, MapItemQueue* queue);
-  uint32_t GenerateAndWriteMapItems(uint32_t offset);
+  uint32_t WriteDebugInfoItems(Stream* stream);
+  uint32_t WriteCodeItems(Stream* stream, bool reserve_only);
+  uint32_t WriteTypeLists(Stream* stream);
+  uint32_t WriteStringDatas(Stream* stream);
+  uint32_t WriteClassDatas(Stream* stream);
+  uint32_t WriteMethodHandles(Stream* stream);
+  uint32_t WriteMapItems(Stream* stream, MapItemQueue* queue);
+  uint32_t GenerateAndWriteMapItems(Stream* stream);
 
-  virtual uint32_t WriteCodeItemPostInstructionData(dex_ir::CodeItem* item,
-                                                    uint32_t offset,
+  virtual uint32_t WriteCodeItemPostInstructionData(Stream* stream,
+                                                    dex_ir::CodeItem* item,
                                                     bool reserve_only);
-  virtual uint32_t WriteCodeItem(dex_ir::CodeItem* item, uint32_t offset, bool reserve_only);
+  virtual uint32_t WriteCodeItem(Stream* stream, dex_ir::CodeItem* item, bool reserve_only);
 
   // Process an offset, if compute_offset is set, write into the dex ir item, otherwise read the
   // existing offset and use that for writing.
-  void ProcessOffset(uint32_t* const offset, dex_ir::Item* item) {
-    if (compute_offsets_) {
-      item->SetOffset(*offset);
-    } else {
-      // Not computing offsets, just use the one in the item.
-      *offset = item->GetOffset();
-    }
-  }
+  void ProcessOffset(Stream* stream, dex_ir::Item* item);
 
   dex_ir::Header* const header_;
-  MemMap* const mem_map_;
   DexLayout* const dex_layout_;
   bool compute_offsets_;
 
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 3d3b121..d33a0bd 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -1813,21 +1813,14 @@
   LayoutCodeItems(dex_file);
 }
 
-void DexLayout::OutputDexFile(const DexFile* dex_file, bool compute_offsets) {
-  const std::string& dex_file_location = dex_file->GetLocation();
+void DexLayout::OutputDexFile(const DexFile* input_dex_file,
+                              bool compute_offsets,
+                              std::unique_ptr<DexContainer>* dex_container) {
+  const std::string& dex_file_location = input_dex_file->GetLocation();
   std::string error_msg;
   std::unique_ptr<File> new_file;
-  // Since we allow dex growth, we need to size the map larger than the original input to be safe.
-  // Reserve an extra 10% to add some buffer room. Note that this is probably more than
-  // necessary.
-  static constexpr size_t kReserveFraction = 10;
-  // Add an extra constant amount since the compact dex header and extra tables may cause more
-  // expansion than fits in the reserve fraction for small dex files.
-  // TODO: Move to using a resizable buffer like a vector.
-  static constexpr size_t kExtraReserve = 128 * KB;
-  const size_t max_size = header_->FileSize() + kExtraReserve +
-      header_->FileSize() / kReserveFraction;
-  if (!options_.output_to_memmap_) {
+  // If options_.output_dex_directory_ is non null, we are outputting to a file.
+  if (options_.output_dex_directory_ != nullptr) {
     std::string output_location(options_.output_dex_directory_);
     size_t last_slash = dex_file_location.rfind('/');
     std::string dex_file_directory = dex_file_location.substr(0, last_slash + 1);
@@ -1843,32 +1836,18 @@
       LOG(ERROR) << "Could not create dex writer output file: " << output_location;
       return;
     }
-    if (ftruncate(new_file->Fd(), max_size) != 0) {
-      LOG(ERROR) << "Could not grow dex writer output file: " << output_location;;
+  }
+  DexWriter::Output(this, dex_container, compute_offsets);
+  DexContainer* const container = dex_container->get();
+  DexContainer::Section* const main_section = container->GetMainSection();
+  DexContainer::Section* const data_section = container->GetDataSection();
+  CHECK_EQ(data_section->Size(), 0u) << "Unsupported";
+  if (new_file != nullptr) {
+    if (!new_file->WriteFully(main_section->Begin(), main_section->Size())) {
+      LOG(ERROR) << "Failed tow write dex file to " << dex_file_location;
       new_file->Erase();
       return;
     }
-    mem_map_.reset(MemMap::MapFile(max_size, PROT_READ | PROT_WRITE, MAP_SHARED,
-        new_file->Fd(), 0, /*low_4gb*/ false, output_location.c_str(), &error_msg));
-  } else {
-    mem_map_.reset(MemMap::MapAnonymous("layout dex", nullptr, max_size,
-        PROT_READ | PROT_WRITE, /* low_4gb */ false, /* reuse */ false, &error_msg));
-  }
-  if (mem_map_ == nullptr) {
-    LOG(ERROR) << "Could not create mem map for dex writer output: " << error_msg;
-    if (new_file != nullptr) {
-      new_file->Erase();
-    }
-    return;
-  }
-  DexWriter::Output(header_, mem_map_.get(), this, compute_offsets, options_.compact_dex_level_);
-  if (new_file != nullptr) {
-    // Since we make the memmap larger than needed, shrink the file back down to not leave extra
-    // padding.
-    int res = new_file->SetLength(header_->FileSize());
-    if (res != 0) {
-      LOG(ERROR) << "Truncating file resulted in " << res;
-    }
     UNUSED(new_file->FlushCloseOrErase());
   }
 }
@@ -1878,8 +1857,11 @@
  */
 void DexLayout::ProcessDexFile(const char* file_name,
                                const DexFile* dex_file,
-                               size_t dex_file_index) {
-  const bool output = options_.output_dex_directory_ != nullptr || options_.output_to_memmap_;
+                               size_t dex_file_index,
+                               std::unique_ptr<DexContainer>* dex_container) {
+  const bool has_output_container = dex_container != nullptr;
+  const bool output = options_.output_dex_directory_ != nullptr || has_output_container;
+
   // Try to avoid eagerly assigning offsets to find bugs since GetOffset will abort if the offset
   // is unassigned.
   bool eagerly_assign_offsets = false;
@@ -1918,22 +1900,29 @@
     if (do_layout) {
       LayoutOutputFile(dex_file);
     }
+    // The output needs a dex container, use a temporary one.
+    std::unique_ptr<DexContainer> temp_container;
+    if (dex_container == nullptr) {
+      dex_container = &temp_container;
+    }
     // If we didn't set the offsets eagerly, we definitely need to compute them here.
-    OutputDexFile(dex_file, do_layout || !eagerly_assign_offsets);
+    OutputDexFile(dex_file, do_layout || !eagerly_assign_offsets, dex_container);
 
     // Clear header before verifying to reduce peak RAM usage.
     const size_t file_size = header_->FileSize();
     header.reset();
 
     // Verify the output dex file's structure, only enabled by default for debug builds.
-    if (options_.verify_output_) {
+    if (options_.verify_output_ && has_output_container) {
       std::string error_msg;
       std::string location = "memory mapped file for " + std::string(file_name);
       // Dex file verifier cannot handle compact dex.
       bool verify = options_.compact_dex_level_ == CompactDexLevel::kCompactDexLevelNone;
       const ArtDexFileLoader dex_file_loader;
+      DexContainer::Section* section = (*dex_container)->GetMainSection();
+      DCHECK_EQ(file_size, section->Size());
       std::unique_ptr<const DexFile> output_dex_file(
-          dex_file_loader.Open(mem_map_->Begin(),
+          dex_file_loader.Open(section->Begin(),
                                file_size,
                                location,
                                /* checksum */ 0,
@@ -1988,7 +1977,8 @@
     fprintf(out_file_, "Checksum verified\n");
   } else {
     for (size_t i = 0; i < dex_files.size(); i++) {
-      ProcessDexFile(file_name, dex_files[i].get(), i);
+      // Pass in a null container to avoid output by default.
+      ProcessDexFile(file_name, dex_files[i].get(), i, /*dex_container*/ nullptr);
     }
   }
   return 0;
diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h
index cb0eabc..00d24db 100644
--- a/dexlayout/dexlayout.h
+++ b/dexlayout/dexlayout.h
@@ -28,6 +28,7 @@
 #include <unordered_map>
 
 #include "dex/compact_dex_level.h"
+#include "dex_container.h"
 #include "dex/dex_file_layout.h"
 #include "dex_ir.h"
 #include "mem_map.h"
@@ -55,7 +56,7 @@
   bool disassemble_ = false;
   bool exports_only_ = false;
   bool ignore_bad_checksum_ = false;
-  bool output_to_memmap_ = false;
+  bool output_to_container_ = false;
   bool show_annotations_ = false;
   bool show_file_headers_ = false;
   bool show_section_headers_ = false;
@@ -82,6 +83,18 @@
 
 class DexLayout {
  public:
+  class VectorOutputContainer {
+   public:
+    // Begin is not necessarily aligned (for now).
+    uint8_t* Begin() {
+      return &data_[0];
+    }
+
+   private:
+    std::vector<uint8_t> data_;
+  };
+
+
   // Setting this to false disables class def layout entirely, which is stronger than strictly
   // necessary to ensure the partial order w.r.t. class derivation. TODO: Re-enable (b/68317550).
   static constexpr bool kChangeClassDefOrder = false;
@@ -89,18 +102,21 @@
   DexLayout(Options& options,
             ProfileCompilationInfo* info,
             FILE* out_file,
-            dex_ir::Header*
-            header = nullptr)
-      : options_(options), info_(info), out_file_(out_file), header_(header) { }
+            dex_ir::Header* header)
+      : options_(options),
+        info_(info),
+        out_file_(out_file),
+        header_(header) { }
 
   int ProcessFile(const char* file_name);
-  void ProcessDexFile(const char* file_name, const DexFile* dex_file, size_t dex_file_index);
+  void ProcessDexFile(const char* file_name,
+                      const DexFile* dex_file,
+                      size_t dex_file_index,
+                      std::unique_ptr<DexContainer>* dex_container);
 
   dex_ir::Header* GetHeader() const { return header_; }
   void SetHeader(dex_ir::Header* header) { header_ = header; }
 
-  MemMap* GetAndReleaseMemMap() { return mem_map_.release(); }
-
   DexLayoutSections& GetSections() {
     return dex_sections_;
   }
@@ -150,7 +166,9 @@
   // Creates a new layout for the dex file based on profile info.
   // Currently reorders ClassDefs, ClassDataItems, and CodeItems.
   void LayoutOutputFile(const DexFile* dex_file);
-  void OutputDexFile(const DexFile* dex_file, bool compute_offsets);
+  void OutputDexFile(const DexFile* input_dex_file,
+                     bool compute_offsets,
+                     std::unique_ptr<DexContainer>* dex_container);
 
   void DumpCFG(const DexFile* dex_file, int idx);
   void DumpCFG(const DexFile* dex_file, uint32_t dex_method_idx, const DexFile::CodeItem* code);
@@ -159,7 +177,6 @@
   ProfileCompilationInfo* info_;
   FILE* out_file_;
   dex_ir::Header* header_;
-  std::unique_ptr<MemMap> mem_map_;
   DexLayoutSections dex_sections_;
   // Layout hotness information is only calculated when dexlayout is enabled.
   DexLayoutHotnessInfo layout_hotness_info_;
diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc
index 83fb99a..ece0f93 100644
--- a/dexlayout/dexlayout_main.cc
+++ b/dexlayout/dexlayout_main.cc
@@ -80,7 +80,7 @@
 
   // Parse all arguments.
   while (1) {
-    const int ic = getopt(argc, argv, "abcdefghil:mo:p:stvw:x:");
+    const int ic = getopt(argc, argv, "abcdefghil:o:p:stvw:x:");
     if (ic < 0) {
       break;  // done
     }
@@ -119,9 +119,6 @@
           want_usage = true;
         }
         break;
-      case 'm':  // output dex files to a memmap
-        options.output_to_memmap_ = true;
-        break;
       case 'o':  // output file
         options.output_file_name_ = optarg;
         break;
@@ -197,7 +194,7 @@
   }
 
   // Create DexLayout instance.
-  DexLayout dex_layout(options, profile_info.get(), out_file);
+  DexLayout dex_layout(options, profile_info.get(), out_file, /*header*/ nullptr);
 
   // Process all files supplied on command line.
   int result = 0;
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc
index bedc457..6d4b3e3 100644
--- a/dexoptanalyzer/dexoptanalyzer.cc
+++ b/dexoptanalyzer/dexoptanalyzer.cc
@@ -260,6 +260,7 @@
     oat_file_assistant = std::make_unique<OatFileAssistant>(dex_file_.c_str(),
                                                             isa_,
                                                             false /*load_executable*/,
+                                                            false /*only_load_system_executable*/,
                                                             vdex_fd_,
                                                             oat_fd_,
                                                             zip_fd_);
diff --git a/dt_fd_forward/dt_fd_forward.cc b/dt_fd_forward/dt_fd_forward.cc
index cf3088d..1cf31a7 100644
--- a/dt_fd_forward/dt_fd_forward.cc
+++ b/dt_fd_forward/dt_fd_forward.cc
@@ -162,7 +162,7 @@
 IOResult FdForwardTransport::ReadUpToMax(void* data, size_t ndata, /*out*/size_t* read_amount) {
   CHECK_GE(read_fd_.get(), 0);
   int avail;
-  int res = ioctl(read_fd_, FIONREAD, &avail);
+  int res = TEMP_FAILURE_RETRY(ioctl(read_fd_, FIONREAD, &avail));
   if (res < 0) {
     DT_IO_ERROR("Failed ioctl(read_fd_, FIONREAD, &avail)");
     return IOResult::kError;
@@ -172,7 +172,7 @@
   if (*read_amount == 0) {
     // Check if the read would cause an EOF.
     struct pollfd pollfd = { read_fd_, POLLRDHUP, 0 };
-    res = poll(&pollfd, /*nfds*/1, /*timeout*/0);
+    res = TEMP_FAILURE_RETRY(poll(&pollfd, /*nfds*/1, /*timeout*/0));
     if (res < 0 || (pollfd.revents & POLLERR) == POLLERR) {
       DT_IO_ERROR("Failed poll on read fd.");
       return IOResult::kError;
@@ -214,13 +214,13 @@
       // No more data. Sleep without locks until more is available. We don't actually check for any
       // errors since possible ones are (1) the read_fd_ is closed or wakeup happens which are both
       // fine since the wakeup_fd_ or the poll failing will wake us up.
-      int poll_res = poll(pollfds, 2, -1);
+      int poll_res = TEMP_FAILURE_RETRY(poll(pollfds, 2, -1));
       if (poll_res < 0) {
         DT_IO_ERROR("Failed to poll!");
       }
       // Clear the wakeup_fd regardless.
       uint64_t val;
-      int unused = read(wakeup_fd_, &val, sizeof(val));
+      int unused = TEMP_FAILURE_RETRY(read(wakeup_fd_, &val, sizeof(val)));
       DCHECK(unused == sizeof(val) || errno == EAGAIN);
       if (poll_res < 0) {
         return IOResult::kError;
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index fcbf2f1..f2a69f3 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1055,14 +1055,19 @@
     os << StringPrintf("checksum: 0x%08x\n", oat_dex_file.GetDexFileLocationChecksum());
 
     const uint8_t* const oat_file_begin = oat_dex_file.GetOatFile()->Begin();
-    const uint8_t* const vdex_file_begin = oat_dex_file.GetOatFile()->DexBegin();
+    if (oat_dex_file.GetOatFile()->ContainsDexCode()) {
+      const uint8_t* const vdex_file_begin = oat_dex_file.GetOatFile()->DexBegin();
 
-    // Print data range of the dex file embedded inside the corresponding vdex file.
-    const uint8_t* const dex_file_pointer = oat_dex_file.GetDexFilePointer();
-    uint32_t dex_offset = dchecked_integral_cast<uint32_t>(dex_file_pointer - vdex_file_begin);
-    os << StringPrintf("dex-file: 0x%08x..0x%08x\n",
-                       dex_offset,
-                       dchecked_integral_cast<uint32_t>(dex_offset + oat_dex_file.FileSize() - 1));
+      // Print data range of the dex file embedded inside the corresponding vdex file.
+      const uint8_t* const dex_file_pointer = oat_dex_file.GetDexFilePointer();
+      uint32_t dex_offset = dchecked_integral_cast<uint32_t>(dex_file_pointer - vdex_file_begin);
+      os << StringPrintf(
+          "dex-file: 0x%08x..0x%08x\n",
+          dex_offset,
+          dchecked_integral_cast<uint32_t>(dex_offset + oat_dex_file.FileSize() - 1));
+    } else {
+      os << StringPrintf("dex-file not in VDEX file\n");
+    }
 
     // Create the dex file early. A lot of print-out things depend on it.
     std::string error_msg;
@@ -3041,8 +3046,15 @@
   return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
 }
 
-static int DumpOat(Runtime* runtime, const char* oat_filename, OatDumperOptions* options,
+static int DumpOat(Runtime* runtime,
+                   const char* oat_filename,
+                   const char* dex_filename,
+                   OatDumperOptions* options,
                    std::ostream* os) {
+  if (dex_filename == nullptr) {
+    LOG(WARNING) << "No dex filename provided, "
+                 << "oatdump might fail if the oat file does not contain the dex code.";
+  }
   std::string error_msg;
   std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_filename,
                                                   oat_filename,
@@ -3050,7 +3062,7 @@
                                                   nullptr,
                                                   false,
                                                   /*low_4gb*/false,
-                                                  nullptr,
+                                                  dex_filename,
                                                   &error_msg));
   if (oat_file == nullptr) {
     fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
@@ -3064,7 +3076,10 @@
   }
 }
 
-static int SymbolizeOat(const char* oat_filename, std::string& output_name, bool no_bits) {
+static int SymbolizeOat(const char* oat_filename,
+                        const char* dex_filename,
+                        std::string& output_name,
+                        bool no_bits) {
   std::string error_msg;
   std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_filename,
                                                   oat_filename,
@@ -3072,7 +3087,7 @@
                                                   nullptr,
                                                   false,
                                                   /*low_4gb*/false,
-                                                  nullptr,
+                                                  dex_filename,
                                                   &error_msg));
   if (oat_file == nullptr) {
     fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
@@ -3102,7 +3117,8 @@
   static bool Dump(Runtime* runtime,
                    const std::string& imt_file,
                    bool dump_imt_stats,
-                   const char* oat_filename) {
+                   const char* oat_filename,
+                   const char* dex_filename) {
     Thread* self = Thread::Current();
 
     ScopedObjectAccess soa(self);
@@ -3118,7 +3134,7 @@
                                                       nullptr,
                                                       false,
                                                       /*low_4gb*/false,
-                                                      nullptr,
+                                                      dex_filename,
                                                       &error_msg));
       if (oat_file == nullptr) {
         fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
@@ -3553,6 +3569,8 @@
 
     if (option.starts_with("--oat-file=")) {
       oat_filename_ = option.substr(strlen("--oat-file=")).data();
+    } else if (option.starts_with("--dex-file=")) {
+      dex_filename_ = option.substr(strlen("--dex-file=")).data();
     } else if (option.starts_with("--image=")) {
       image_location_ = option.substr(strlen("--image=")).data();
     } else if (option == "--no-dump:vmap") {
@@ -3704,6 +3722,7 @@
 
  public:
   const char* oat_filename_ = nullptr;
+  const char* dex_filename_ = nullptr;
   const char* class_filter_ = "";
   const char* method_filter_ = "";
   const char* image_location_ = nullptr;
@@ -3764,10 +3783,12 @@
       // This is what "strip --only-keep-debug" does when it creates separate ELF file
       // with only debug data. We use it in similar way to exclude .rodata and .text.
       bool no_bits = args_->only_keep_debug_;
-      return SymbolizeOat(args_->oat_filename_, args_->output_name_, no_bits) == EXIT_SUCCESS;
+      return SymbolizeOat(args_->oat_filename_, args_->dex_filename_, args_->output_name_, no_bits)
+          == EXIT_SUCCESS;
     } else {
       return DumpOat(nullptr,
                      args_->oat_filename_,
+                     args_->dex_filename_,
                      oat_dumper_options_.get(),
                      args_->os_) == EXIT_SUCCESS;
     }
@@ -3780,12 +3801,14 @@
       return IMTDumper::Dump(runtime,
                              args_->imt_dump_,
                              args_->imt_stat_dump_,
-                             args_->oat_filename_);
+                             args_->oat_filename_,
+                             args_->dex_filename_);
     }
 
     if (args_->oat_filename_ != nullptr) {
       return DumpOat(runtime,
                      args_->oat_filename_,
+                     args_->dex_filename_,
                      oat_dumper_options_.get(),
                      args_->os_) == EXIT_SUCCESS;
     }
diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc
index 53d8483..9e11a25 100644
--- a/openjdkjvmti/deopt_manager.cc
+++ b/openjdkjvmti/deopt_manager.cc
@@ -38,11 +38,11 @@
 #include "base/enums.h"
 #include "base/mutex-inl.h"
 #include "dex/dex_file_annotations.h"
+#include "dex/modifiers.h"
 #include "events-inl.h"
 #include "jni_internal.h"
 #include "mirror/class-inl.h"
 #include "mirror/object_array-inl.h"
-#include "modifiers.h"
 #include "nativehelper/scoped_local_ref.h"
 #include "runtime_callbacks.h"
 #include "scoped_thread_state_change-inl.h"
diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc
index dcc834a..323137a 100644
--- a/openjdkjvmti/fixed_up_dex_file.cc
+++ b/openjdkjvmti/fixed_up_dex_file.cc
@@ -35,6 +35,7 @@
 #include "dex/dex_file_loader.h"
 
 // Runtime includes.
+#include "dex_container.h"
 #include "dex/compact_dex_level.h"
 #include "dex_to_dex_decompiler.h"
 #include "dexlayout.h"
@@ -92,18 +93,21 @@
   if (original.IsCompactDexFile()) {
     // Since we are supposed to return a standard dex, convert back using dexlayout.
     art::Options options;
-    options.output_to_memmap_ = true;
     options.compact_dex_level_ = art::CompactDexLevel::kCompactDexLevelNone;
     options.update_checksum_ = true;
-    art::DexLayout dex_layout(options, nullptr, nullptr);
-    dex_layout.ProcessDexFile(new_dex_file->GetLocation().c_str(), new_dex_file.get(), 0);
-    std::unique_ptr<art::MemMap> mem_map(dex_layout.GetAndReleaseMemMap());
-
-    const uint32_t dex_file_size =
-        reinterpret_cast<const art::DexFile::Header*>(mem_map->Begin())->file_size_;
+    art::DexLayout dex_layout(options,
+                              /*info*/ nullptr,
+                              /*out_file*/ nullptr,
+                              /*header*/ nullptr);
+    std::unique_ptr<art::DexContainer> dex_container;
+    dex_layout.ProcessDexFile(new_dex_file->GetLocation().c_str(),
+                              new_dex_file.get(),
+                              0,
+                              &dex_container);
+    art::DexContainer::Section* main_section = dex_container->GetMainSection();
     // Overwrite the dex file stored in data with the new result.
     data.clear();
-    data.insert(data.end(), mem_map->Begin(), mem_map->Begin() + dex_file_size);
+    data.insert(data.end(), main_section->Begin(), main_section->End());
     new_dex_file = dex_file_loader.Open(
         data.data(),
         data.size(),
diff --git a/openjdkjvmti/ti_breakpoint.cc b/openjdkjvmti/ti_breakpoint.cc
index fa7a344..d5fffdf 100644
--- a/openjdkjvmti/ti_breakpoint.cc
+++ b/openjdkjvmti/ti_breakpoint.cc
@@ -39,11 +39,11 @@
 #include "base/mutex-inl.h"
 #include "deopt_manager.h"
 #include "dex/dex_file_annotations.h"
+#include "dex/modifiers.h"
 #include "events-inl.h"
 #include "jni_internal.h"
 #include "mirror/class-inl.h"
 #include "mirror/object_array-inl.h"
-#include "modifiers.h"
 #include "nativehelper/scoped_local_ref.h"
 #include "runtime_callbacks.h"
 #include "scoped_thread_state_change-inl.h"
diff --git a/openjdkjvmti/ti_field.cc b/openjdkjvmti/ti_field.cc
index db5c31c..c016966 100644
--- a/openjdkjvmti/ti_field.cc
+++ b/openjdkjvmti/ti_field.cc
@@ -35,9 +35,9 @@
 #include "art_jvmti.h"
 #include "base/enums.h"
 #include "dex/dex_file_annotations.h"
+#include "dex/modifiers.h"
 #include "jni_internal.h"
 #include "mirror/object_array-inl.h"
-#include "modifiers.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-current-inl.h"
 
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index 57fb699..3f144c8 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -40,6 +40,7 @@
 #include "dex/code_item_accessors-inl.h"
 #include "dex/dex_file_annotations.h"
 #include "dex/dex_file_types.h"
+#include "dex/modifiers.h"
 #include "events-inl.h"
 #include "jit/jit.h"
 #include "jni_internal.h"
@@ -47,7 +48,6 @@
 #include "mirror/class_loader.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
-#include "modifiers.h"
 #include "nativehelper/scoped_local_ref.h"
 #include "oat_file.h"
 #include "runtime_callbacks.h"
diff --git a/profman/profman.cc b/profman/profman.cc
index 9f3e3b6..ffc3c01 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -149,6 +149,10 @@
   UsageError("  --boot-image-sampled-method-threshold=<value>: minimum number of profiles a");
   UsageError("      non-hot method needs to be in order to be hot in the output profile. The");
   UsageError("      default is max int.");
+  UsageError("  --copy-and-update-profile-key: if present, profman will copy the profile from");
+  UsageError("      the file passed with --profile-fd(file) to the profile passed with");
+  UsageError("      --reference-profile-fd(file) and update at the same time the profile-key");
+  UsageError("      of entries corresponding to the apks passed with --apk(-fd).");
   UsageError("");
 
   exit(EXIT_FAILURE);
@@ -186,7 +190,8 @@
       test_profile_method_percerntage_(kDefaultTestProfileMethodPercentage),
       test_profile_class_percentage_(kDefaultTestProfileClassPercentage),
       test_profile_seed_(NanoTime()),
-      start_ns_(NanoTime()) {}
+      start_ns_(NanoTime()),
+      copy_and_update_profile_key_(false) {}
 
   ~ProfMan() {
     LogCompletionTime();
@@ -302,11 +307,13 @@
             "should only be used together");
     }
     ProfileAssistant::ProcessingResult result;
+
     if (profile_files_.empty()) {
       // The file doesn't need to be flushed here (ProcessProfiles will do it)
       // so don't check the usage.
       File file(reference_profile_file_fd_, false);
-      result = ProfileAssistant::ProcessProfiles(profile_files_fd_, reference_profile_file_fd_);
+      result = ProfileAssistant::ProcessProfiles(profile_files_fd_,
+                                                 reference_profile_file_fd_);
       CloseAllFds(profile_files_fd_, "profile_files_fd_");
     } else {
       result = ProfileAssistant::ProcessProfiles(profile_files_, reference_profile_file_);
@@ -314,7 +321,7 @@
     return result;
   }
 
-  void OpenApkFilesFromLocations(std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+  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) {
       // Get the APKs from the collection of FDs.
@@ -1070,6 +1077,42 @@
     return !test_profile_.empty();
   }
 
+  bool ShouldCopyAndUpdateProfileKey() const {
+    return copy_and_update_profile_key_;
+  }
+
+  bool CopyAndUpdateProfileKey() const {
+    // Validate that at least one profile file was passed, as well as a reference profile.
+    if (!(profile_files_.size() == 1 ^ profile_files_fd_.size() == 1)) {
+      Usage("Only one profile file should be specified.");
+    }
+    if (reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
+      Usage("No reference profile file specified.");
+    }
+
+    if (apk_files_.empty() && apks_fd_.empty()) {
+      Usage("No apk files specified");
+    }
+
+    bool use_fds = profile_files_fd_.size() == 1;
+
+    ProfileCompilationInfo profile;
+    // Do not clear if invalid. The input might be an archive.
+    if (profile.Load(profile_files_[0], /*clear_if_invalid*/ false)) {
+      // Open the dex files to look up classes and methods.
+      std::vector<std::unique_ptr<const DexFile>> dex_files;
+      OpenApkFilesFromLocations(&dex_files);
+      if (!profile.UpdateProfileKeys(dex_files)) {
+        return false;
+      }
+      return use_fds
+        ? profile.Save(reference_profile_file_fd_)
+        : profile.Save(reference_profile_file_, /*bytes_written*/ nullptr);
+    } else {
+      return false;
+    }
+  }
+
  private:
   static void ParseFdForCollection(const StringPiece& option,
                                    const char* arg_name,
@@ -1114,6 +1157,7 @@
   uint16_t test_profile_class_percentage_;
   uint32_t test_profile_seed_;
   uint64_t start_ns_;
+  bool copy_and_update_profile_key_;
 };
 
 // See ProfileAssistant::ProcessingResult for return codes.
diff --git a/runtime/Android.bp b/runtime/Android.bp
index aba2b0e..f2f7c3e 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -14,12 +14,6 @@
 // limitations under the License.
 //
 
-// Keep the __jit_debug_register_code symbol as a unique symbol during ICF for architectures where
-// we use gold as the linker (arm, x86, x86_64). The symbol is used by the debuggers to detect when
-// new jit code is generated. We don't want it to be called when a different function with the same
-// (empty) body is called.
-JIT_DEBUG_REGISTER_CODE_LDFLAGS = ["-Wl,--keep-unique,__jit_debug_register_code"]
-
 cc_defaults {
     name: "libdexfile_defaults",
     defaults: ["art_defaults"],
@@ -33,6 +27,7 @@
         "dex/dex_file_tracking_registrar.cc",
         "dex/dex_file_verifier.cc",
         "dex/dex_instruction.cc",
+        "dex/modifiers.cc",
         "dex/standard_dex_file.cc",
         "utf.cc",
         "utils.cc",
@@ -56,16 +51,8 @@
             ],
         },
     },
-    header_libs: [
-        "jni_headers",
-    ],
     generated_sources: ["art_operator_srcs"],
-    // asm_support_gen.h (used by asm_support.h) is generated with cpp-define-generator
-    generated_headers: ["cpp-define-generator-asm-support"],
-    // export our headers so the libart-gtest targets can use it as well.
-    export_generated_headers: ["cpp-define-generator-asm-support"],
     include_dirs: [
-        "external/icu/icu4c/source/common",
         "external/zlib",
     ],
     shared_libs: [
@@ -78,11 +65,20 @@
     // Exporting "." would shadow the system elf.h with our elf.h,
     // which in turn breaks any tools that reference this library.
     // export_include_dirs: ["."],
+}
 
-    // ART's macros.h depends on libbase's macros.h.
-    // Note: runtime_options.h depends on cmdline. But we don't really want to export this
-    //       generically. dex2oat takes care of it itself.
-    export_shared_lib_headers: ["libbase"],
+gensrcs {
+    name: "dexfile_operator_srcs",
+    cmd: "$(location generate-operator-out.py) art/runtime $(in) > $(out)",
+    tool_files: ["generate-operator-out.py"],
+    srcs: [
+        "dex/dex_file.h",
+        "dex/dex_file_layout.h",
+        "dex/dex_instruction.h",
+        "dex/dex_instruction_utils.h",
+        "dex/invoke_type.h",
+    ],
+    output_extension: "operator_out.cc",
 }
 
 art_cc_library {
@@ -95,6 +91,12 @@
     },
 }
 
+// Keep the __jit_debug_register_code symbol as a unique symbol during ICF for architectures where
+// we use gold as the linker (arm, x86, x86_64). The symbol is used by the debuggers to detect when
+// new jit code is generated. We don't want it to be called when a different function with the same
+// (empty) body is called.
+JIT_DEBUG_REGISTER_CODE_LDFLAGS = ["-Wl,--keep-unique,__jit_debug_register_code"]
+
 cc_defaults {
     name: "libart_defaults",
     defaults: ["art_defaults"],
@@ -142,6 +144,7 @@
         "dex/dex_file_tracking_registrar.cc",
         "dex/dex_file_verifier.cc",
         "dex/dex_instruction.cc",
+        "dex/modifiers.cc",
         "dex/standard_dex_file.cc",
         "dex_to_dex_decompiler.cc",
         "elf_file.cc",
@@ -535,6 +538,7 @@
         "dex/dex_file_layout.h",
         "dex/dex_instruction.h",
         "dex/dex_instruction_utils.h",
+        "dex/invoke_type.h",
         "gc_root.h",
         "gc/allocator_type.h",
         "gc/allocator/rosalloc.h",
@@ -547,7 +551,6 @@
         "image.h",
         "instrumentation.h",
         "indirect_reference_table.h",
-        "invoke_type.h",
         "jdwp_provider.h",
         "jdwp/jdwp.h",
         "jdwp/jdwp_constants.h",
diff --git a/runtime/arch/mips/entrypoints_direct_mips.h b/runtime/arch/mips/entrypoints_direct_mips.h
index 1020781..3a6625f 100644
--- a/runtime/arch/mips/entrypoints_direct_mips.h
+++ b/runtime/arch/mips/entrypoints_direct_mips.h
@@ -54,6 +54,7 @@
       entrypoint == kQuickAsin ||
       entrypoint == kQuickAtan ||
       entrypoint == kQuickAtan2 ||
+      entrypoint == kQuickPow ||
       entrypoint == kQuickCbrt ||
       entrypoint == kQuickCosh ||
       entrypoint == kQuickExp ||
diff --git a/runtime/art_field.h b/runtime/art_field.h
index 46b013d..0eeeef2 100644
--- a/runtime/art_field.h
+++ b/runtime/art_field.h
@@ -20,8 +20,8 @@
 #include <jni.h>
 
 #include "dex/dex_file_types.h"
+#include "dex/modifiers.h"
 #include "gc_root.h"
-#include "modifiers.h"
 #include "obj_ptr.h"
 #include "offsets.h"
 #include "primitive.h"
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index c9a7733..65bacd8 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -27,8 +27,9 @@
 #include "dex/dex_file-inl.h"
 #include "dex/dex_file_annotations.h"
 #include "dex/dex_file_types.h"
+#include "dex/invoke_type.h"
 #include "gc_root-inl.h"
-#include "invoke_type.h"
+#include "intrinsics_enum.h"
 #include "jit/profiling_info.h"
 #include "mirror/class-inl.h"
 #include "mirror/dex_cache-inl.h"
@@ -412,7 +413,11 @@
     DCHECK_EQ(is_default_conflict, IsDefaultConflicting());
     DCHECK_EQ(is_compilable, IsCompilable());
     DCHECK_EQ(must_count_locks, MustCountLocks());
-    DCHECK_EQ(hidden_api_list, GetHiddenApiAccessFlags());
+    // We need to special case java.lang.ref.Reference.getRefererent. The Java method
+    // is hidden but we do not yet have a way of making intrinsics hidden.
+    if (intrinsic != static_cast<uint32_t>(Intrinsics::kReferenceGetReferent)) {
+      DCHECK_EQ(hidden_api_list, GetHiddenApiAccessFlags());
+    }
   } else {
     SetAccessFlags(new_value);
   }
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 4501450..ce8e8ac 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -31,8 +31,8 @@
 #include "dex/code_item_accessors.h"
 #include "dex/dex_file.h"
 #include "dex/dex_instruction_iterator.h"
+#include "dex/modifiers.h"
 #include "gc_root.h"
-#include "modifiers.h"
 #include "obj_ptr.h"
 #include "offsets.h"
 #include "primitive.h"
diff --git a/runtime/base/file_utils.cc b/runtime/base/file_utils.cc
index 63b4ac5..d22fd99 100644
--- a/runtime/base/file_utils.cc
+++ b/runtime/base/file_utils.cc
@@ -353,4 +353,9 @@
   return 0;
 }
 
+bool LocationIsOnSystem(const char* location) {
+  UniqueCPtr<const char[]> path(realpath(location, nullptr));
+  return path != nullptr && android::base::StartsWith(path.get(), GetAndroidRoot().c_str());
+}
+
 }  // namespace art
diff --git a/runtime/base/file_utils.h b/runtime/base/file_utils.h
index e4555ad..cac0950 100644
--- a/runtime/base/file_utils.h
+++ b/runtime/base/file_utils.h
@@ -82,6 +82,9 @@
 // Madvise the largest page aligned region within begin and end.
 int MadviseLargestPageAlignedRegion(const uint8_t* begin, const uint8_t* end, int advice);
 
+// Return whether the location is on system (i.e. android root).
+bool LocationIsOnSystem(const char* location);
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_BASE_FILE_UTILS_H_
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 92d8651..03774f4 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -26,7 +26,7 @@
 #include "class_linker-inl.h"
 #include "dex/dex_file-inl.h"
 #include "dex/dex_instruction-inl.h"
-#include "invoke_type.h"
+#include "dex/invoke_type.h"
 #include "mirror/class-inl.h"
 #include "mirror/method_type.h"
 #include "mirror/object-inl.h"
diff --git a/runtime/dex/dex_file.h b/runtime/dex/dex_file.h
index 1ee48f7..511ce31 100644
--- a/runtime/dex/dex_file.h
+++ b/runtime/dex/dex_file.h
@@ -995,6 +995,10 @@
     return Begin() <= addr && addr < Begin() + Size();
   }
 
+  DexFileContainer* GetContainer() const {
+    return container_.get();
+  }
+
  protected:
   // First Dex format version supporting default methods.
   static const uint32_t kDefaultMethodsVersion = 37;
diff --git a/runtime/dex/dex_file_verifier.cc b/runtime/dex/dex_file_verifier.cc
index 7265aad..f7fdbb0 100644
--- a/runtime/dex/dex_file_verifier.cc
+++ b/runtime/dex/dex_file_verifier.cc
@@ -27,6 +27,7 @@
 #include "dex_file-inl.h"
 #include "experimental_flags.h"
 #include "leb128.h"
+#include "modifiers.h"
 #include "safe_map.h"
 #include "utf-inl.h"
 #include "utils.h"
@@ -453,6 +454,7 @@
 
   uint32_t count = map->size_;
   uint32_t last_offset = 0;
+  uint32_t last_type = 0;
   uint32_t data_item_count = 0;
   uint32_t data_items_left = header_->data_size_;
   uint32_t used_bits = 0;
@@ -465,7 +467,11 @@
   // Check the items listed in the map.
   for (uint32_t i = 0; i < count; i++) {
     if (UNLIKELY(last_offset >= item->offset_ && i != 0)) {
-      ErrorStringPrintf("Out of order map item: %x then %x", last_offset, item->offset_);
+      ErrorStringPrintf("Out of order map item: %x then %x for type %x last type was %x",
+                        last_offset,
+                        item->offset_,
+                        static_cast<uint32_t>(item->type_),
+                        last_type);
       return false;
     }
     if (UNLIKELY(item->offset_ >= header_->file_size_)) {
@@ -501,6 +507,7 @@
 
     used_bits |= bit;
     last_offset = item->offset_;
+    last_type = item->type_;
     item++;
   }
 
diff --git a/runtime/invoke_type.h b/runtime/dex/invoke_type.h
similarity index 89%
rename from runtime/invoke_type.h
rename to runtime/dex/invoke_type.h
index 2b877e6..726d269 100644
--- a/runtime/invoke_type.h
+++ b/runtime/dex/invoke_type.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_INVOKE_TYPE_H_
-#define ART_RUNTIME_INVOKE_TYPE_H_
+#ifndef ART_RUNTIME_DEX_INVOKE_TYPE_H_
+#define ART_RUNTIME_DEX_INVOKE_TYPE_H_
 
 #include <iosfwd>
 
@@ -35,4 +35,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_INVOKE_TYPE_H_
+#endif  // ART_RUNTIME_DEX_INVOKE_TYPE_H_
diff --git a/runtime/dex/modifiers.cc b/runtime/dex/modifiers.cc
new file mode 100644
index 0000000..30daefb
--- /dev/null
+++ b/runtime/dex/modifiers.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2011 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 <string>
+
+#include "modifiers.h"
+
+namespace art {
+
+std::string PrettyJavaAccessFlags(uint32_t access_flags) {
+  std::string result;
+  if ((access_flags & kAccPublic) != 0) {
+    result += "public ";
+  }
+  if ((access_flags & kAccProtected) != 0) {
+    result += "protected ";
+  }
+  if ((access_flags & kAccPrivate) != 0) {
+    result += "private ";
+  }
+  if ((access_flags & kAccFinal) != 0) {
+    result += "final ";
+  }
+  if ((access_flags & kAccStatic) != 0) {
+    result += "static ";
+  }
+  if ((access_flags & kAccAbstract) != 0) {
+    result += "abstract ";
+  }
+  if ((access_flags & kAccInterface) != 0) {
+    result += "interface ";
+  }
+  if ((access_flags & kAccTransient) != 0) {
+    result += "transient ";
+  }
+  if ((access_flags & kAccVolatile) != 0) {
+    result += "volatile ";
+  }
+  if ((access_flags & kAccSynchronized) != 0) {
+    result += "synchronized ";
+  }
+  return result;
+}
+
+}  // namespace art
diff --git a/runtime/modifiers.h b/runtime/dex/modifiers.h
similarity index 96%
rename from runtime/modifiers.h
rename to runtime/dex/modifiers.h
index 0e2db93..2998f60 100644
--- a/runtime/modifiers.h
+++ b/runtime/dex/modifiers.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_MODIFIERS_H_
-#define ART_RUNTIME_MODIFIERS_H_
+#ifndef ART_RUNTIME_DEX_MODIFIERS_H_
+#define ART_RUNTIME_DEX_MODIFIERS_H_
 
 #include <stdint.h>
 
@@ -138,7 +138,11 @@
 
 static constexpr uint32_t kAccVisibilityFlags = kAccPublic | kAccPrivate | kAccProtected;
 
+// Returns a human-readable version of the Java part of the access flags, e.g., "private static "
+// (note the trailing whitespace).
+std::string PrettyJavaAccessFlags(uint32_t access_flags);
+
 }  // namespace art
 
-#endif  // ART_RUNTIME_MODIFIERS_H_
+#endif  // ART_RUNTIME_DEX_MODIFIERS_H_
 
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 3048f45..9ef7d42 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -25,12 +25,12 @@
 #include "class_linker-inl.h"
 #include "common_throws.h"
 #include "dex/dex_file.h"
+#include "dex/invoke_type.h"
 #include "entrypoints/quick/callee_save_frame.h"
 #include "handle_scope-inl.h"
 #include "imt_conflict_table.h"
 #include "imtable-inl.h"
 #include "indirect_reference_table.h"
-#include "invoke_type.h"
 #include "jni_internal.h"
 #include "mirror/array.h"
 #include "mirror/class-inl.h"
diff --git a/runtime/hidden_api_access_flags.h b/runtime/hidden_api_access_flags.h
index 80a002d..c328f96 100644
--- a/runtime/hidden_api_access_flags.h
+++ b/runtime/hidden_api_access_flags.h
@@ -18,7 +18,7 @@
 #define ART_RUNTIME_HIDDEN_API_ACCESS_FLAGS_H_
 
 #include "base/bit_utils.h"
-#include "modifiers.h"
+#include "dex/modifiers.h"
 
 namespace art {
 
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 33fa0d6..4bf2895 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -2030,4 +2030,28 @@
   return memcmp(buffer, kProfileMagic, byte_count) == 0;
 }
 
+bool ProfileCompilationInfo::UpdateProfileKeys(
+      const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
+  for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
+    for (DexFileData* dex_data : info_) {
+      if (dex_data->checksum == dex_file->GetLocationChecksum()
+          && dex_data->num_method_ids == dex_file->NumMethodIds()) {
+        std::string new_profile_key = GetProfileDexFileKey(dex_file->GetLocation());
+        if (dex_data->profile_key != new_profile_key) {
+          if (profile_key_map_.find(new_profile_key) != profile_key_map_.end()) {
+            // We can't update the key if the new key belongs to a different dex file.
+            LOG(ERROR) << "Cannot update profile key to " << new_profile_key
+                << " because the new key belongs to another dex file.";
+            return false;
+          }
+          profile_key_map_.erase(dex_data->profile_key);
+          profile_key_map_.Put(new_profile_key, dex_data->profile_index);
+          dex_data->profile_key = new_profile_key;
+        }
+      }
+    }
+  }
+  return true;
+}
+
 }  // namespace art
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index 29a4c11..350ce9e 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -416,6 +416,17 @@
   // Return true if the fd points to a profile file.
   bool IsProfileFile(int fd);
 
+  // Update the profile keys corresponding to the given dex files based on their current paths.
+  // This method allows fix-ups in the profile for dex files that might have been renamed.
+  // The new profile key will be constructed based on the current dex location.
+  //
+  // The matching [profile key <-> dex_file] is done based on the dex checksum and the number of
+  // methods ids. If neither is a match then the profile key is not updated.
+  //
+  // If the new profile key would collide with an existing key (for a different dex)
+  // the method returns false. Otherwise it returns true.
+  bool UpdateProfileKeys(const std::vector<std::unique_ptr<const DexFile>>& dex_files);
+
  private:
   enum ProfileLoadStatus {
     kProfileLoadWouldOverwiteData,
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index 6ce9bcb..b4265d1 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -22,6 +22,7 @@
 #include "class_linker-inl.h"
 #include "common_runtime_test.h"
 #include "dex/dex_file.h"
+#include "dex/dex_file_loader.h"
 #include "handle_scope-inl.h"
 #include "jit/profile_compilation_info.h"
 #include "linear_alloc.h"
@@ -286,16 +287,15 @@
 
     // Prepare the profile content for zipping.
     ASSERT_TRUE(profile.GetFile()->ResetOffset());
-    uint64_t data_size = profile.GetFile()->GetLength();
-    std::unique_ptr<uint8_t> data(new uint8_t[data_size]);
-    ASSERT_TRUE(profile.GetFile()->ReadFully(data.get(), data_size));
+    std::vector<uint8_t> data(profile.GetFile()->GetLength());
+    ASSERT_TRUE(profile.GetFile()->ReadFully(data.data(), data.size()));
 
     // Zip the profile content.
     ScratchFile zip;
     FILE* file = fopen(zip.GetFile()->GetPath().c_str(), "wb");
     ZipWriter writer(file);
     writer.StartEntry(zip_entry, zip_flags);
-    writer.WriteBytes(data.get(), data_size);
+    writer.WriteBytes(data.data(), data.size());
     writer.FinishEntry();
     writer.Finish();
     fflush(file);
@@ -1019,16 +1019,15 @@
 
   // Prepare the profile content for zipping.
   ASSERT_TRUE(profile.GetFile()->ResetOffset());
-  uint64_t data_size = profile.GetFile()->GetLength();
-  std::unique_ptr<uint8_t> data(new uint8_t[data_size]);
-  ASSERT_TRUE(profile.GetFile()->ReadFully(data.get(), data_size));
+  std::vector<uint8_t> data(profile.GetFile()->GetLength());
+  ASSERT_TRUE(profile.GetFile()->ReadFully(data.data(), data.size()));
 
   // Zip the profile content.
   ScratchFile zip;
   FILE* file = fopen(zip.GetFile()->GetPath().c_str(), "wb");
   ZipWriter writer(file);
-  writer.StartEntry("primary.prof", ZipWriter::kCompress | ZipWriter::kAlign32);
-  writer.WriteBytes(data.get(), data_size);
+  writer.StartEntry("primary.prof", ZipWriter::kAlign32);
+  writer.WriteBytes(data.data(), data.size());
   writer.FinishEntry();
   writer.Finish();
   fflush(file);
@@ -1040,4 +1039,89 @@
   ASSERT_FALSE(loaded_info.Load(GetFd(zip)));
 }
 
+TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyOk) {
+  std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("MultiDex");
+
+  ProfileCompilationInfo info;
+  for (const std::unique_ptr<const DexFile>& dex : dex_files) {
+    // Create the profile with a different location so that we can update it to the
+    // real dex location later.
+    std::string base_location = DexFileLoader::GetBaseLocation(dex->GetLocation());
+    std::string multidex_suffix = DexFileLoader::GetMultiDexSuffix(dex->GetLocation());
+    std::string old_name = base_location + "-old" + multidex_suffix;
+    info.AddMethodIndex(Hotness::kFlagHot,
+                        old_name,
+                        dex->GetLocationChecksum(),
+                        /* method_idx */ 0,
+                        dex->NumMethodIds());
+  }
+
+  // Update the profile keys based on the original dex files
+  ASSERT_TRUE(info.UpdateProfileKeys(dex_files));
+
+  // Verify that we find the methods when searched with the original dex files.
+  for (const std::unique_ptr<const DexFile>& dex : dex_files) {
+    std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi =
+        info.GetMethod(dex->GetLocation(), dex->GetLocationChecksum(), /* method_idx */ 0);
+    ASSERT_TRUE(loaded_pmi != nullptr);
+  }
+}
+
+TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyOkButNoUpdate) {
+  std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("MultiDex");
+
+  ProfileCompilationInfo info;
+  info.AddMethodIndex(Hotness::kFlagHot,
+                      "my.app",
+                      /* checksum */ 123,
+                      /* method_idx */ 0,
+                      /* num_method_ids */ 10);
+
+  // Update the profile keys based on the original dex files
+  ASSERT_TRUE(info.UpdateProfileKeys(dex_files));
+
+  // Verify that we did not perform any update and that we cannot find anything with the new
+  // location.
+  for (const std::unique_ptr<const DexFile>& dex : dex_files) {
+    std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi =
+        info.GetMethod(dex->GetLocation(), dex->GetLocationChecksum(), /* method_idx */ 0);
+    ASSERT_TRUE(loaded_pmi == nullptr);
+  }
+
+  // Verify that we can find the original entry.
+  std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi =
+        info.GetMethod("my.app", /* checksum */ 123, /* method_idx */ 0);
+  ASSERT_TRUE(loaded_pmi != nullptr);
+}
+
+TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyFail) {
+  std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("MultiDex");
+
+
+  ProfileCompilationInfo info;
+  // Add all dex
+  for (const std::unique_ptr<const DexFile>& dex : dex_files) {
+    // Create the profile with a different location so that we can update it to the
+    // real dex location later.
+    std::string base_location = DexFileLoader::GetBaseLocation(dex->GetLocation());
+    std::string multidex_suffix = DexFileLoader::GetMultiDexSuffix(dex->GetLocation());
+    std::string old_name = base_location + "-old" + multidex_suffix;
+    info.AddMethodIndex(Hotness::kFlagHot,
+                        old_name,
+                        dex->GetLocationChecksum(),
+                        /* method_idx */ 0,
+                        dex->NumMethodIds());
+  }
+
+  // Add a method index using the location we want to rename to.
+  // This will cause the rename to fail because an existing entry would already have that name.
+  info.AddMethodIndex(Hotness::kFlagHot,
+                      dex_files[0]->GetLocation(),
+                      /* checksum */ 123,
+                      /* method_idx */ 0,
+                      dex_files[0]->NumMethodIds());
+
+  ASSERT_FALSE(info.UpdateProfileKeys(dex_files));
+}
+
 }  // namespace art
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 302a5e6..cd313b3 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -26,11 +26,11 @@
 #include "class_linker.h"
 #include "class_loader.h"
 #include "common_throws.h"
-#include "dex_cache.h"
 #include "dex/dex_file-inl.h"
+#include "dex/invoke_type.h"
+#include "dex_cache.h"
 #include "gc/heap-inl.h"
 #include "iftable.h"
-#include "invoke_type.h"
 #include "subtype_check.h"
 #include "object-inl.h"
 #include "object_array.h"
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 84b0326..ced7c7c 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -25,10 +25,10 @@
 #include "class_status.h"
 #include "dex/dex_file.h"
 #include "dex/dex_file_types.h"
+#include "dex/modifiers.h"
 #include "gc/allocator_type.h"
 #include "gc_root.h"
 #include "imtable.h"
-#include "modifiers.h"
 #include "object.h"
 #include "object_array.h"
 #include "primitive.h"
diff --git a/runtime/mirror/field.h b/runtime/mirror/field.h
index 6845575..dd09be3 100644
--- a/runtime/mirror/field.h
+++ b/runtime/mirror/field.h
@@ -19,8 +19,8 @@
 
 #include "accessible_object.h"
 #include "base/enums.h"
+#include "dex/modifiers.h"
 #include "gc_root.h"
-#include "modifiers.h"
 #include "obj_ptr.h"
 #include "object.h"
 #include "primitive.h"
diff --git a/runtime/mirror/method_handles_lookup.cc b/runtime/mirror/method_handles_lookup.cc
index a390a2e..039bbf2 100644
--- a/runtime/mirror/method_handles_lookup.cc
+++ b/runtime/mirror/method_handles_lookup.cc
@@ -17,11 +17,11 @@
 #include "method_handles_lookup.h"
 
 #include "class-inl.h"
+#include "dex/modifiers.h"
 #include "gc_root-inl.h"
 #include "handle_scope.h"
 #include "jni_internal.h"
 #include "mirror/method_handle_impl.h"
-#include "modifiers.h"
 #include "object-inl.h"
 #include "well_known_classes.h"
 
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index c03dbcc..9fd9905 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -620,14 +620,6 @@
                                 dex_file_location.c_str());
       return false;
     }
-    if (UNLIKELY(dex_file_offset == 0U)) {
-      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with zero dex "
-                                    "file offset",
-                                GetLocation().c_str(),
-                                i,
-                                dex_file_location.c_str());
-      return false;
-    }
     if (UNLIKELY(dex_file_offset > DexSize())) {
       *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file "
                                     "offset %u > %zu",
@@ -638,20 +630,45 @@
                                 DexSize());
       return false;
     }
-    if (UNLIKELY(DexSize() - dex_file_offset < sizeof(DexFile::Header))) {
-      *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file "
-                                    "offset %u of %zu but the size of dex file header is %zu",
-                                GetLocation().c_str(),
-                                i,
-                                dex_file_location.c_str(),
-                                dex_file_offset,
-                                DexSize(),
-                                sizeof(DexFile::Header));
-      return false;
+    const uint8_t* dex_file_pointer = nullptr;
+    if (UNLIKELY(dex_file_offset == 0U)) {
+      if (uncompressed_dex_files_ == nullptr) {
+        uncompressed_dex_files_.reset(new std::vector<std::unique_ptr<const DexFile>>());
+        // No dex files, load it from location.
+        const ArtDexFileLoader dex_file_loader;
+        if (!dex_file_loader.Open(dex_file_location.c_str(),
+                                  dex_file_location,
+                                  /* verify */ false,
+                                  /* verify_checksum */ false,
+                                  error_msg,
+                                  uncompressed_dex_files_.get())) {
+          if (Runtime::Current() == nullptr) {
+            // If there's no runtime, we're running oatdump, so return
+            // a half constructed oat file that oatdump knows how to deal with.
+            LOG(WARNING) << "Could not find associated dex files of oat file. "
+                         << "Oatdump will only dump the header.";
+            return true;
+          } else {
+            return false;
+          }
+        }
+      }
+      dex_file_pointer = uncompressed_dex_files_.get()->at(i)->Begin();
+    } else {
+      if (UNLIKELY(DexSize() - dex_file_offset < sizeof(DexFile::Header))) {
+        *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file "
+                                      "offset %u of %zu but the size of dex file header is %zu",
+                                  GetLocation().c_str(),
+                                  i,
+                                  dex_file_location.c_str(),
+                                  dex_file_offset,
+                                  DexSize(),
+                                  sizeof(DexFile::Header));
+        return false;
+      }
+      dex_file_pointer = DexBegin() + dex_file_offset;
     }
 
-    const uint8_t* dex_file_pointer = DexBegin() + dex_file_offset;
-
     const bool valid_magic = DexFileLoader::IsMagicValid(dex_file_pointer);
     if (UNLIKELY(!valid_magic)) {
       *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with invalid "
@@ -672,7 +689,7 @@
       return false;
     }
     const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(dex_file_pointer);
-    if (DexSize() - dex_file_offset < header->file_size_) {
+    if (dex_file_offset != 0 && (DexSize() - dex_file_offset < header->file_size_)) {
       *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file "
                                     "offset %u and size %u truncated at %zu",
                                 GetLocation().c_str(),
@@ -1297,13 +1314,15 @@
 
 std::string OatFile::ResolveRelativeEncodedDexLocation(
       const char* abs_dex_location, const std::string& rel_dex_location) {
-  if (abs_dex_location != nullptr && rel_dex_location[0] != '/') {
+  // For host, we still do resolution as the rel_dex_location might be absolute
+  // for a target dex (for example /system/foo/foo.apk).
+  if (abs_dex_location != nullptr && (rel_dex_location[0] != '/' || !kIsTargetBuild)) {
     // Strip :classes<N>.dex used for secondary multidex files.
     std::string base = DexFileLoader::GetBaseLocation(rel_dex_location);
     std::string multidex_suffix = DexFileLoader::GetMultiDexSuffix(rel_dex_location);
 
     // Check if the base is a suffix of the provided abs_dex_location.
-    std::string target_suffix = "/" + base;
+    std::string target_suffix = ((rel_dex_location[0] != '/') ? "/" : "") + base;
     std::string abs_location(abs_dex_location);
     if (abs_location.size() > target_suffix.size()) {
       size_t pos = abs_location.size() - target_suffix.size();
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index bf22e0b..50a706d 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -328,6 +328,11 @@
     return vdex_.get();
   }
 
+  // Whether the OatFile embeds the Dex code.
+  bool ContainsDexCode() const {
+    return uncompressed_dex_files_ == nullptr;
+  }
+
  protected:
   OatFile(const std::string& filename, bool executable);
 
@@ -399,6 +404,10 @@
   // elements. std::list<> and std::deque<> satisfy this requirement, std::vector<> doesn't.
   mutable std::list<std::string> string_cache_ GUARDED_BY(secondary_lookup_lock_);
 
+  // Cache of dex files mapped directly from a location, in case the OatFile does
+  // not embed the dex code.
+  std::unique_ptr<std::vector<std::unique_ptr<const DexFile>>> uncompressed_dex_files_;
+
   friend class gc::collector::DummyOatFile;  // For modifying begin_ and end_.
   friend class OatClass;
   friend class art::OatDexFile;
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 73ca19a..15a5954 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -72,9 +72,12 @@
 
 OatFileAssistant::OatFileAssistant(const char* dex_location,
                                    const InstructionSet isa,
-                                   bool load_executable)
+                                   bool load_executable,
+                                   bool only_load_system_executable)
     : OatFileAssistant(dex_location,
-                       isa, load_executable,
+                       isa,
+                       load_executable,
+                       only_load_system_executable,
                        -1 /* vdex_fd */,
                        -1 /* oat_fd */,
                        -1 /* zip_fd */) {}
@@ -83,11 +86,13 @@
 OatFileAssistant::OatFileAssistant(const char* dex_location,
                                    const InstructionSet isa,
                                    bool load_executable,
+                                   bool only_load_system_executable,
                                    int vdex_fd,
                                    int oat_fd,
                                    int zip_fd)
     : isa_(isa),
       load_executable_(load_executable),
+      only_load_system_executable_(only_load_system_executable),
       odex_(this, /*is_oat_location*/ false),
       oat_(this, /*is_oat_location*/ true),
       zip_fd_(zip_fd) {
@@ -1122,6 +1127,10 @@
   if (!load_attempted_) {
     load_attempted_ = true;
     if (filename_provided_) {
+      bool executable = oat_file_assistant_->load_executable_;
+      if (executable && oat_file_assistant_->only_load_system_executable_) {
+        executable = LocationIsOnSystem(filename_.c_str());
+      }
       std::string error_msg;
       if (use_fd_) {
         if (oat_fd_ >= 0 && vdex_fd_ >= 0) {
@@ -1130,7 +1139,7 @@
                                     filename_.c_str(),
                                     nullptr,
                                     nullptr,
-                                    oat_file_assistant_->load_executable_,
+                                    executable,
                                     false /* low_4gb */,
                                     oat_file_assistant_->dex_location_.c_str(),
                                     &error_msg));
@@ -1140,7 +1149,7 @@
                                   filename_.c_str(),
                                   nullptr,
                                   nullptr,
-                                  oat_file_assistant_->load_executable_,
+                                  executable,
                                   false /* low_4gb */,
                                   oat_file_assistant_->dex_location_.c_str(),
                                   &error_msg));
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 6c01c1e..a614030 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -119,9 +119,13 @@
   //
   // load_executable should be true if the caller intends to try and load
   // executable code for this dex location.
+  //
+  // only_load_system_executable should be true if the caller intends to have
+  // only oat files from /system loaded executable.
   OatFileAssistant(const char* dex_location,
                    const InstructionSet isa,
-                   bool load_executable);
+                   bool load_executable,
+                   bool only_load_system_executable = false);
 
   // Similar to this(const char*, const InstructionSet, bool), however, if a valid zip_fd is
   // provided, vdex, oat, and zip files will be read from vdex_fd, oat_fd and zip_fd respectively.
@@ -129,6 +133,7 @@
   OatFileAssistant(const char* dex_location,
                    const InstructionSet isa,
                    bool load_executable,
+                   bool only_load_system_executable,
                    int vdex_fd,
                    int oat_fd,
                    int zip_fd);
@@ -487,6 +492,9 @@
   // Whether we will attempt to load oat files executable.
   bool load_executable_ = false;
 
+  // Whether only oat files on /system are loaded executable.
+  const bool only_load_system_executable_ = false;
+
   // Cached value of the required dex checksums.
   // This should be accessed only by the GetRequiredDexChecksums() method.
   std::vector<uint32_t> cached_required_dex_checksums_;
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index a98da0f..50f5e7a 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -246,6 +246,7 @@
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
                                       kRuntimeISA,
                                       false,
+                                      false,
                                       vdex_fd.get(),
                                       odex_fd.get(),
                                       zip_fd.get());
@@ -285,6 +286,7 @@
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
                                       kRuntimeISA,
                                       false,
+                                      false,
                                       vdex_fd.get(),
                                       -1 /* oat_fd */,
                                       zip_fd.get());
@@ -319,6 +321,7 @@
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
                                       kRuntimeISA,
                                       false,
+                                      false,
                                       -1 /* vdex_fd */,
                                       odex_fd.get(),
                                       zip_fd.get());
@@ -342,6 +345,7 @@
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
                                       kRuntimeISA,
                                       false,
+                                      false,
                                       -1 /* vdex_fd */,
                                       -1 /* oat_fd */,
                                       zip_fd);
@@ -1439,6 +1443,60 @@
                 default_filter, false, false, relative_context.get()));
 }
 
+TEST_F(OatFileAssistantTest, SystemOdex) {
+  std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
+  std::string odex_location = GetScratchDir() + "/OatUpToDate.odex";
+  std::string system_location = GetAndroidRoot() + "/OatUpToDate.jar";
+
+  std::string error_msg;
+
+  Copy(GetDexSrc1(), dex_location);
+  EXPECT_FALSE(LocationIsOnSystem(dex_location.c_str()));
+
+  {
+    OatFileAssistant oat_file_assistant(dex_location.c_str(),
+                                        kRuntimeISA,
+                                        true,
+                                        false);
+    int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
+    EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+    EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable());
+  }
+
+  {
+    OatFileAssistant oat_file_assistant(dex_location.c_str(),
+                                        kRuntimeISA,
+                                        true,
+                                        true);
+    int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
+    EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+    EXPECT_FALSE(oat_file_assistant.GetBestOatFile()->IsExecutable());
+  }
+
+  Copy(GetDexSrc1(), system_location);
+  EXPECT_TRUE(LocationIsOnSystem(system_location.c_str()));
+
+  {
+    OatFileAssistant oat_file_assistant(system_location.c_str(),
+                                        kRuntimeISA,
+                                        true,
+                                        false);
+    int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
+    EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+    EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable());
+  }
+
+  {
+    OatFileAssistant oat_file_assistant(system_location.c_str(),
+                                        kRuntimeISA,
+                                        true,
+                                        true);
+    int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
+    EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+    EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable());
+  }
+}
+
 // TODO: More Tests:
 //  * Test class linker falls back to unquickened dex for DexNoOat
 //  * Test class linker falls back to unquickened dex for MultiDexNoOat
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 9503360..e419444 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -56,15 +56,11 @@
 // If true, we attempt to load the application image if it exists.
 static constexpr bool kEnableAppImage = true;
 
-static bool OatFileIsOnSystem(const std::unique_ptr<const OatFile>& oat_file) {
-  UniqueCPtr<const char[]> path(realpath(oat_file->GetLocation().c_str(), nullptr));
-  return path != nullptr && android::base::StartsWith(oat_file->GetLocation(),
-                                                      GetAndroidRoot().c_str());
-}
-
 const OatFile* OatFileManager::RegisterOatFile(std::unique_ptr<const OatFile> oat_file) {
   WriterMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
-  CHECK(!only_use_system_oat_files_ || OatFileIsOnSystem(oat_file))
+  CHECK(!only_use_system_oat_files_ ||
+        LocationIsOnSystem(oat_file->GetLocation().c_str()) ||
+        !oat_file->IsExecutable())
       << "Registering a non /system oat file: " << oat_file->GetLocation();
   DCHECK(oat_file != nullptr);
   if (kIsDebugBuild) {
@@ -424,7 +420,8 @@
 
   OatFileAssistant oat_file_assistant(dex_location,
                                       kRuntimeISA,
-                                      !runtime->IsAotCompiler());
+                                      !runtime->IsAotCompiler(),
+                                      only_use_system_oat_files_);
 
   // Lock the target oat location to avoid races generating and loading the
   // oat file.
@@ -437,8 +434,7 @@
 
   const OatFile* source_oat_file = nullptr;
 
-  // No point in trying to make up-to-date if we can only use system oat files.
-  if (!only_use_system_oat_files_ && !oat_file_assistant.IsUpToDate()) {
+  if (!oat_file_assistant.IsUpToDate()) {
     // Update the oat file on disk if we can, based on the --compiler-filter
     // option derived from the current runtime options.
     // This may fail, but that's okay. Best effort is all that matters here.
@@ -474,9 +470,7 @@
   // Get the oat file on disk.
   std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());
 
-  if (oat_file != nullptr && only_use_system_oat_files_ && !OatFileIsOnSystem(oat_file)) {
-    // If the oat file is not on /system, don't use it.
-  } else  if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) {
+  if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) {
     // Prevent oat files from being loaded if no class_loader or dex_elements are provided.
     // This can happen when the deprecated DexFile.<init>(String) is called directly, and it
     // could load oat files without checking the classpath, which would be incorrect.
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index dd6b7ba..038474e 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -127,6 +127,9 @@
 
   std::set<std::unique_ptr<const OatFile>> oat_files_ GUARDED_BY(Locks::oat_file_manager_lock_);
   bool have_non_pic_oat_file_;
+
+  // Only use the compiled code in an OAT file when the file is on /system. If the OAT file
+  // is not on /system, don't load it "executable".
   bool only_use_system_oat_files_;
 
   DISALLOW_COPY_AND_ASSIGN(OatFileManager);
diff --git a/runtime/runtime_intrinsics.cc b/runtime/runtime_intrinsics.cc
index f710ebe..3295a86 100644
--- a/runtime/runtime_intrinsics.cc
+++ b/runtime/runtime_intrinsics.cc
@@ -18,8 +18,8 @@
 
 #include "art_method-inl.h"
 #include "class_linker.h"
+#include "dex/invoke_type.h"
 #include "intrinsics_enum.h"
-#include "invoke_type.h"
 #include "mirror/class.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
diff --git a/runtime/utf.cc b/runtime/utf.cc
index 93fcb32..32ae187 100644
--- a/runtime/utf.cc
+++ b/runtime/utf.cc
@@ -18,8 +18,7 @@
 
 #include <android-base/logging.h>
 
-#include "mirror/array.h"
-#include "mirror/object-inl.h"
+#include "base/casts.h"
 #include "utf-inl.h"
 
 namespace art {
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 79ddcb9..b2ec669 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -30,7 +30,6 @@
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
-#include "dex/dex_file-inl.h"
 #include "os.h"
 #include "utf-inl.h"
 
@@ -126,41 +125,6 @@
   return result;
 }
 
-std::string PrettyJavaAccessFlags(uint32_t access_flags) {
-  std::string result;
-  if ((access_flags & kAccPublic) != 0) {
-    result += "public ";
-  }
-  if ((access_flags & kAccProtected) != 0) {
-    result += "protected ";
-  }
-  if ((access_flags & kAccPrivate) != 0) {
-    result += "private ";
-  }
-  if ((access_flags & kAccFinal) != 0) {
-    result += "final ";
-  }
-  if ((access_flags & kAccStatic) != 0) {
-    result += "static ";
-  }
-  if ((access_flags & kAccAbstract) != 0) {
-    result += "abstract ";
-  }
-  if ((access_flags & kAccInterface) != 0) {
-    result += "interface ";
-  }
-  if ((access_flags & kAccTransient) != 0) {
-    result += "transient ";
-  }
-  if ((access_flags & kAccVolatile) != 0) {
-    result += "volatile ";
-  }
-  if ((access_flags & kAccSynchronized) != 0) {
-    result += "synchronized ";
-  }
-  return result;
-}
-
 std::string PrettySize(int64_t byte_count) {
   // The byte thresholds at which we display amounts.  A byte count is displayed
   // in unit U when kUnitThresholds[U] <= bytes < kUnitThresholds[U+1].
diff --git a/runtime/utils.h b/runtime/utils.h
index 7402c12..abdafcc 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -82,10 +82,6 @@
 std::string PrettyDescriptor(const char* descriptor);
 std::string PrettyDescriptor(Primitive::Type type);
 
-// Returns a human-readable version of the Java part of the access flags, e.g., "private static "
-// (note the trailing whitespace).
-std::string PrettyJavaAccessFlags(uint32_t access_flags);
-
 // Returns a human-readable size string such as "1MB".
 std::string PrettySize(int64_t size_in_bytes);
 
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 4e45128..202380d 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -218,6 +218,10 @@
   ArrayRef<const uint8_t> GetQuickenedInfoOf(const DexFile& dex_file,
                                              uint32_t dex_method_idx) const;
 
+  bool HasDexSection() const {
+    return GetHeader().GetDexSize() != 0;
+  }
+
  private:
   uint32_t GetQuickeningInfoTableOffset(const uint8_t* source_dex_begin) const;
 
@@ -235,10 +239,6 @@
       uint32_t num_method_ids,
       const ArrayRef<const uint8_t>& quickening_info) const;
 
-  bool HasDexSection() const {
-    return GetHeader().GetDexSize() != 0;
-  }
-
   bool ContainsDexFile(const DexFile& dex_file) const;
 
   const uint8_t* DexBegin() const {
diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc
index f3d4d77..2caed4b 100644
--- a/runtime/zip_archive.cc
+++ b/runtime/zip_archive.cc
@@ -29,6 +29,7 @@
 
 #include "base/bit_utils.h"
 #include "base/unix_file/fd_file.h"
+#include "dex/dex_file.h"
 
 namespace art {
 
@@ -49,11 +50,15 @@
   return zip_entry_->method == kCompressStored;
 }
 
-bool ZipEntry::IsAlignedTo(size_t alignment) {
+bool ZipEntry::IsAlignedTo(size_t alignment) const {
   DCHECK(IsPowerOfTwo(alignment)) << alignment;
   return IsAlignedParam(zip_entry_->offset, static_cast<int>(alignment));
 }
 
+bool ZipEntry::IsAlignedToDexHeader() const {
+  return IsAlignedTo(alignof(DexFile::Header));
+}
+
 ZipEntry::~ZipEntry() {
   delete zip_entry_;
 }
diff --git a/runtime/zip_archive.h b/runtime/zip_archive.h
index 75f8757..70518e1 100644
--- a/runtime/zip_archive.h
+++ b/runtime/zip_archive.h
@@ -59,7 +59,8 @@
   uint32_t GetCrc32();
 
   bool IsUncompressed();
-  bool IsAlignedTo(size_t alignment);
+  bool IsAlignedTo(size_t alignment) const;
+  bool IsAlignedToDexHeader() const;
 
  private:
   ZipEntry(ZipArchiveHandle handle,
diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java
index 98838c5..f6332b5 100644
--- a/test/530-checker-lse/src/Main.java
+++ b/test/530-checker-lse/src/Main.java
@@ -398,6 +398,7 @@
   /// CHECK-START: int Main.test15() load_store_elimination (after)
   /// CHECK: <<Const2:i\d+>> IntConstant 2
   /// CHECK: StaticFieldSet
+  /// CHECK: StaticFieldSet
   /// CHECK-NOT: StaticFieldGet
   /// CHECK: Return [<<Const2>>]
 
@@ -772,127 +773,6 @@
     return obj;
   }
 
-  /// CHECK-START: void Main.testStoreStore2(TestClass2) load_store_elimination (before)
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldSet
-
-  /// CHECK-START: void Main.testStoreStore2(TestClass2) load_store_elimination (after)
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldSet
-  /// CHECK-NOT: InstanceFieldSet
-
-  private static void testStoreStore2(TestClass2 obj) {
-    obj.i = 41;
-    obj.j = 42;
-    obj.i = 43;
-    obj.j = 44;
-  }
-
-  /// CHECK-START: void Main.testStoreStore3(TestClass2, boolean) load_store_elimination (before)
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldSet
-
-  /// CHECK-START: void Main.testStoreStore3(TestClass2, boolean) load_store_elimination (after)
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldSet
-  /// CHECK-NOT: InstanceFieldSet
-
-  private static void testStoreStore3(TestClass2 obj, boolean flag) {
-    obj.i = 41;
-    obj.j = 42;    // redundant since it's overwritten in both branches below.
-    if (flag) {
-      obj.j = 43;
-    } else {
-      obj.j = 44;
-    }
-  }
-
-  /// CHECK-START: void Main.testStoreStore4() load_store_elimination (before)
-  /// CHECK: StaticFieldSet
-  /// CHECK: StaticFieldSet
-
-  /// CHECK-START: void Main.testStoreStore4() load_store_elimination (after)
-  /// CHECK: StaticFieldSet
-  /// CHECK-NOT: StaticFieldSet
-
-  private static void testStoreStore4() {
-    TestClass.si = 61;
-    TestClass.si = 62;
-  }
-
-  /// CHECK-START: int Main.testStoreStore5(TestClass2, TestClass2) load_store_elimination (before)
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldGet
-  /// CHECK: InstanceFieldSet
-
-  /// CHECK-START: int Main.testStoreStore5(TestClass2, TestClass2) load_store_elimination (after)
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldGet
-  /// CHECK: InstanceFieldSet
-
-  private static int testStoreStore5(TestClass2 obj1, TestClass2 obj2) {
-    obj1.i = 71;      // This store is needed since obj2.i may load from it.
-    int i = obj2.i;
-    obj1.i = 72;
-    return i;
-  }
-
-  /// CHECK-START: int Main.testStoreStore6(TestClass2, TestClass2) load_store_elimination (before)
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldGet
-  /// CHECK: InstanceFieldSet
-
-  /// CHECK-START: int Main.testStoreStore6(TestClass2, TestClass2) load_store_elimination (after)
-  /// CHECK-NOT: InstanceFieldSet
-  /// CHECK: InstanceFieldGet
-  /// CHECK: InstanceFieldSet
-
-  private static int testStoreStore6(TestClass2 obj1, TestClass2 obj2) {
-    obj1.i = 81;      // This store is not needed since obj2.j cannot load from it.
-    int j = obj2.j;
-    obj1.i = 82;
-    return j;
-  }
-
-  /// CHECK-START: int Main.testNoSideEffects(int[]) load_store_elimination (before)
-  /// CHECK: ArraySet
-  /// CHECK: ArraySet
-  /// CHECK: ArraySet
-  /// CHECK: ArrayGet
-
-  /// CHECK-START: int Main.testNoSideEffects(int[]) load_store_elimination (after)
-  /// CHECK: ArraySet
-  /// CHECK: ArraySet
-  /// CHECK-NOT: ArraySet
-  /// CHECK-NOT: ArrayGet
-
-  private static int testNoSideEffects(int[] array) {
-    array[0] = 101;
-    array[1] = 102;
-    int bitCount = Integer.bitCount(0x3456);
-    array[1] = 103;
-    return array[0] + bitCount;
-  }
-
-  /// CHECK-START: void Main.testThrow(TestClass2, java.lang.Exception) load_store_elimination (before)
-  /// CHECK: InstanceFieldSet
-  /// CHECK: Throw
-
-  /// CHECK-START: void Main.testThrow(TestClass2, java.lang.Exception) load_store_elimination (after)
-  /// CHECK: InstanceFieldSet
-  /// CHECK: Throw
-
-  // Make sure throw keeps the store.
-  private static void testThrow(TestClass2 obj, Exception e) throws Exception {
-    obj.i = 55;
-    throw e;
-  }
-
   /// CHECK-START: int Main.testStoreStoreWithDeoptimize(int[]) load_store_elimination (before)
   /// CHECK: NewInstance
   /// CHECK: InstanceFieldSet
@@ -934,6 +814,23 @@
     return arr[0] + arr[1] + arr[2] + arr[3];
   }
 
+  /// CHECK-START: int Main.testNoSideEffects(int[]) load_store_elimination (before)
+  /// CHECK: ArraySet
+  /// CHECK: ArraySet
+  /// CHECK: ArrayGet
+
+  /// CHECK-START: int Main.testNoSideEffects(int[]) load_store_elimination (after)
+  /// CHECK: ArraySet
+  /// CHECK: ArraySet
+  /// CHECK-NOT: ArrayGet
+
+  private static int testNoSideEffects(int[] array) {
+    array[0] = 101;
+    int bitCount = Integer.bitCount(0x3456);
+    array[1] = array[0] + 1;
+    return array[0] + bitCount;
+  }
+
   /// CHECK-START: double Main.getCircleArea(double, boolean) load_store_elimination (before)
   /// CHECK: NewInstance
 
@@ -1208,46 +1105,16 @@
 
     assertIntEquals(testStoreStore().i, 41);
     assertIntEquals(testStoreStore().j, 43);
+    assertIntEquals(testStoreStoreWithDeoptimize(new int[4]), 4);
 
     assertIntEquals(testExitMerge(true), 2);
     assertIntEquals(testExitMerge2(true), 2);
     assertIntEquals(testExitMerge2(false), 2);
 
-    TestClass2 testclass2 = new TestClass2();
-    testStoreStore2(testclass2);
-    assertIntEquals(testclass2.i, 43);
-    assertIntEquals(testclass2.j, 44);
-
-    testStoreStore3(testclass2, true);
-    assertIntEquals(testclass2.i, 41);
-    assertIntEquals(testclass2.j, 43);
-    testStoreStore3(testclass2, false);
-    assertIntEquals(testclass2.i, 41);
-    assertIntEquals(testclass2.j, 44);
-
-    testStoreStore4();
-    assertIntEquals(TestClass.si, 62);
-
-    int ret = testStoreStore5(testclass2, testclass2);
-    assertIntEquals(testclass2.i, 72);
-    assertIntEquals(ret, 71);
-
-    testclass2.j = 88;
-    ret = testStoreStore6(testclass2, testclass2);
-    assertIntEquals(testclass2.i, 82);
-    assertIntEquals(ret, 88);
-
-    ret = testNoSideEffects(iarray);
+    int ret = testNoSideEffects(iarray);
     assertIntEquals(iarray[0], 101);
-    assertIntEquals(iarray[1], 103);
+    assertIntEquals(iarray[1], 102);
     assertIntEquals(ret, 108);
-
-    try {
-      testThrow(testclass2, new Exception());
-    } catch (Exception e) {}
-    assertIntEquals(testclass2.i, 55);
-
-    assertIntEquals(testStoreStoreWithDeoptimize(new int[4]), 4);
   }
 
   static boolean sFlag;
diff --git a/test/608-checker-unresolved-lse/src/Main.java b/test/608-checker-unresolved-lse/src/Main.java
index a39dd51..c6f8854 100644
--- a/test/608-checker-unresolved-lse/src/Main.java
+++ b/test/608-checker-unresolved-lse/src/Main.java
@@ -88,6 +88,7 @@
 
   /// CHECK-START: void Main.staticFieldTest() load_store_elimination (after)
   /// CHECK:        StaticFieldSet
+  /// CHECK:        StaticFieldSet
   /// CHECK:        UnresolvedStaticFieldGet
   public static void staticFieldTest() {
     // Ensure Foo is initialized.
diff --git a/test/639-checker-code-sinking/expected.txt b/test/639-checker-code-sinking/expected.txt
index 5d4833a..52e756c 100644
--- a/test/639-checker-code-sinking/expected.txt
+++ b/test/639-checker-code-sinking/expected.txt
@@ -1,3 +1,3 @@
 0
 class java.lang.Object
-42
+43
diff --git a/test/639-checker-code-sinking/src/Main.java b/test/639-checker-code-sinking/src/Main.java
index a1c30f7..7496925 100644
--- a/test/639-checker-code-sinking/src/Main.java
+++ b/test/639-checker-code-sinking/src/Main.java
@@ -337,7 +337,7 @@
   public static void testStoreStore(boolean doThrow) {
     Main m = new Main();
     m.intField = 42;
-    m.intField2 = 43;
+    m.intField = 43;
     if (doThrow) {
       throw new Error(m.$opt$noinline$toString());
     }
@@ -349,7 +349,6 @@
 
   volatile int volatileField;
   int intField;
-  int intField2;
   Object objectField;
   static boolean doThrow;
   static boolean doLoop;
diff --git a/test/674-hiddenapi/api-blacklist.txt b/test/674-hiddenapi/api-blacklist.txt
new file mode 100644
index 0000000..d43360c
--- /dev/null
+++ b/test/674-hiddenapi/api-blacklist.txt
@@ -0,0 +1,25 @@
+LNullaryConstructorBlacklist;-><init>()V
+LParentClass;->fieldPublicBlacklist:I
+LParentClass;->fieldPackageBlacklist:I
+LParentClass;->fieldProtectedBlacklist:I
+LParentClass;->fieldPrivateBlacklist:I
+LParentClass;->fieldPublicStaticBlacklist:I
+LParentClass;->fieldPackageStaticBlacklist:I
+LParentClass;->fieldProtectedStaticBlacklist:I
+LParentClass;->fieldPrivateStaticBlacklist:I
+LParentClass;->methodPublicBlacklist()I
+LParentClass;->methodPackageBlacklist()I
+LParentClass;->methodProtectedBlacklist()I
+LParentClass;->methodPrivateBlacklist()I
+LParentClass;->methodPublicStaticBlacklist()I
+LParentClass;->methodPackageStaticBlacklist()I
+LParentClass;->methodProtectedStaticBlacklist()I
+LParentClass;->methodPrivateStaticBlacklist()I
+LParentClass;-><init>(IC)V
+LParentClass;-><init>(FC)V
+LParentClass;-><init>(JC)V
+LParentClass;-><init>(DC)V
+LParentInterface;->fieldPublicStaticBlacklist:I
+LParentInterface;->methodPublicBlacklist()I
+LParentInterface;->methodPublicStaticBlacklist()I
+LParentInterface;->methodPublicDefaultBlacklist()I
\ No newline at end of file
diff --git a/test/674-hiddenapi/api-dark-greylist.txt b/test/674-hiddenapi/api-dark-greylist.txt
new file mode 100644
index 0000000..d0f35f6
--- /dev/null
+++ b/test/674-hiddenapi/api-dark-greylist.txt
@@ -0,0 +1,25 @@
+LNullaryConstructorDarkGreylist;-><init>()V
+LParentClass;->fieldPublicDarkGreylist:I
+LParentClass;->fieldPackageDarkGreylist:I
+LParentClass;->fieldProtectedDarkGreylist:I
+LParentClass;->fieldPrivateDarkGreylist:I
+LParentClass;->fieldPublicStaticDarkGreylist:I
+LParentClass;->fieldPackageStaticDarkGreylist:I
+LParentClass;->fieldProtectedStaticDarkGreylist:I
+LParentClass;->fieldPrivateStaticDarkGreylist:I
+LParentClass;->methodPublicDarkGreylist()I
+LParentClass;->methodPackageDarkGreylist()I
+LParentClass;->methodProtectedDarkGreylist()I
+LParentClass;->methodPrivateDarkGreylist()I
+LParentClass;->methodPublicStaticDarkGreylist()I
+LParentClass;->methodPackageStaticDarkGreylist()I
+LParentClass;->methodProtectedStaticDarkGreylist()I
+LParentClass;->methodPrivateStaticDarkGreylist()I
+LParentClass;-><init>(IB)V
+LParentClass;-><init>(FB)V
+LParentClass;-><init>(JB)V
+LParentClass;-><init>(DB)V
+LParentInterface;->fieldPublicStaticDarkGreylist:I
+LParentInterface;->methodPublicDarkGreylist()I
+LParentInterface;->methodPublicStaticDarkGreylist()I
+LParentInterface;->methodPublicDefaultDarkGreylist()I
\ No newline at end of file
diff --git a/test/674-hiddenapi/api-light-greylist.txt b/test/674-hiddenapi/api-light-greylist.txt
new file mode 100644
index 0000000..2809025
--- /dev/null
+++ b/test/674-hiddenapi/api-light-greylist.txt
@@ -0,0 +1,25 @@
+LNullaryConstructorLightGreylist;-><init>()V
+LParentClass;->fieldPublicLightGreylist:I
+LParentClass;->fieldPackageLightGreylist:I
+LParentClass;->fieldProtectedLightGreylist:I
+LParentClass;->fieldPrivateLightGreylist:I
+LParentClass;->fieldPublicStaticLightGreylist:I
+LParentClass;->fieldPackageStaticLightGreylist:I
+LParentClass;->fieldProtectedStaticLightGreylist:I
+LParentClass;->fieldPrivateStaticLightGreylist:I
+LParentClass;->methodPublicLightGreylist()I
+LParentClass;->methodPackageLightGreylist()I
+LParentClass;->methodProtectedLightGreylist()I
+LParentClass;->methodPrivateLightGreylist()I
+LParentClass;->methodPublicStaticLightGreylist()I
+LParentClass;->methodPackageStaticLightGreylist()I
+LParentClass;->methodProtectedStaticLightGreylist()I
+LParentClass;->methodPrivateStaticLightGreylist()I
+LParentClass;-><init>(IZ)V
+LParentClass;-><init>(FZ)V
+LParentClass;-><init>(JZ)V
+LParentClass;-><init>(DZ)V
+LParentInterface;->fieldPublicStaticLightGreylist:I
+LParentInterface;->methodPublicLightGreylist()I
+LParentInterface;->methodPublicStaticLightGreylist()I
+LParentInterface;->methodPublicDefaultLightGreylist()I
\ No newline at end of file
diff --git a/test/674-hiddenapi/build b/test/674-hiddenapi/build
new file mode 100644
index 0000000..330a6de
--- /dev/null
+++ b/test/674-hiddenapi/build
@@ -0,0 +1,38 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+set -e
+
+# Build the jars twice. First with applying hiddenapi, creating a boot jar, then
+# a second time without to create a normal jar. We need to do this because we
+# want to load the jar once as an app module and once as a member of the boot
+# class path. The DexFileVerifier would fail on the former as it does not allow
+# hidden API access flags in dex files. DexFileVerifier is not invoked on boot
+# class path dex files, so the boot jar loads fine in the latter case.
+
+export USE_HIDDENAPI=true
+./default-build "$@"
+
+# Move the jar file into the resource folder to be bundled with the test.
+mkdir res
+mv ${TEST_NAME}.jar res/boot.jar
+
+# Clear all intermediate files otherwise default-build would either skip
+# compilation or fail rebuilding.
+rm -rf classes*
+
+export USE_HIDDENAPI=false
+./default-build "$@"
diff --git a/test/674-hiddenapi/check b/test/674-hiddenapi/check
new file mode 100644
index 0000000..c319a0a
--- /dev/null
+++ b/test/674-hiddenapi/check
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# 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.
+
+# Remove pid and date from the log messages.
+grep -vE '^dalvikvm(32|64) E [^]]+]' "$2" \
+    | grep -v JNI_OnLoad \
+    | grep -v JNI_OnUnload \
+    > "$2.tmp"
+
+./default-check "$1" "$2.tmp"
diff --git a/test/674-hiddenapi/expected.txt b/test/674-hiddenapi/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/674-hiddenapi/expected.txt
diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc
new file mode 100644
index 0000000..672079b
--- /dev/null
+++ b/test/674-hiddenapi/hiddenapi.cc
@@ -0,0 +1,291 @@
+/*
+ * 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.
+ */
+
+#include "class_linker.h"
+#include "dex/art_dex_file_loader.h"
+#include "jni.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread.h"
+#include "ti-agent/scoped_utf_chars.h"
+
+namespace art {
+namespace Test674HiddenApi {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_appendToBootClassLoader(
+    JNIEnv* env, jclass, jstring jpath) {
+  ScopedUtfChars utf(env, jpath);
+  const char* path = utf.c_str();
+  if (path == nullptr) {
+    return;
+  }
+
+  ArtDexFileLoader dex_loader;
+  std::string error_msg;
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  if (!dex_loader.Open(path,
+                       path,
+                       /* verify */ false,
+                       /* verify_checksum */ true,
+                       &error_msg,
+                       &dex_files)) {
+    LOG(FATAL) << "Could not open " << path << " for boot classpath extension: " << error_msg;
+    UNREACHABLE();
+  }
+
+  ScopedObjectAccess soa(Thread::Current());
+  for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
+    Runtime::Current()->GetClassLinker()->AppendToBootClassPath(
+        Thread::Current(), *dex_file.release());
+  }
+}
+
+static jobject NewInstance(JNIEnv* env, jclass klass) {
+  jmethodID constructor = env->GetMethodID(klass, "<init>", "()V");
+  if (constructor == NULL) {
+    return NULL;
+  }
+  return env->NewObject(klass, constructor);
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canDiscoverField(
+    JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
+  ScopedUtfChars utf_name(env, name);
+  jfieldID field = is_static ? env->GetStaticFieldID(klass, utf_name.c_str(), "I")
+                             : env->GetFieldID(klass, utf_name.c_str(), "I");
+  if (field == NULL) {
+    env->ExceptionClear();
+    return JNI_FALSE;
+  }
+
+  return JNI_TRUE;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canGetField(
+    JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
+  ScopedUtfChars utf_name(env, name);
+  jfieldID field = is_static ? env->GetStaticFieldID(klass, utf_name.c_str(), "I")
+                             : env->GetFieldID(klass, utf_name.c_str(), "I");
+  if (field == NULL) {
+    env->ExceptionClear();
+    return JNI_FALSE;
+  }
+  if (is_static) {
+    env->GetStaticIntField(klass, field);
+  } else {
+    jobject obj = NewInstance(env, klass);
+    if (obj == NULL) {
+      env->ExceptionDescribe();
+      env->ExceptionClear();
+      return JNI_FALSE;
+    }
+    env->GetIntField(obj, field);
+  }
+
+  if (env->ExceptionOccurred()) {
+    env->ExceptionDescribe();
+    env->ExceptionClear();
+    return JNI_FALSE;
+  }
+
+  return JNI_TRUE;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canSetField(
+    JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
+  ScopedUtfChars utf_name(env, name);
+  jfieldID field = is_static ? env->GetStaticFieldID(klass, utf_name.c_str(), "I")
+                             : env->GetFieldID(klass, utf_name.c_str(), "I");
+  if (field == NULL) {
+    env->ExceptionClear();
+    return JNI_FALSE;
+  }
+  if (is_static) {
+    env->SetStaticIntField(klass, field, 42);
+  } else {
+    jobject obj = NewInstance(env, klass);
+    if (obj == NULL) {
+      env->ExceptionDescribe();
+      env->ExceptionClear();
+      return JNI_FALSE;
+    }
+    env->SetIntField(obj, field, 42);
+  }
+
+  if (env->ExceptionOccurred()) {
+    env->ExceptionDescribe();
+    env->ExceptionClear();
+    return JNI_FALSE;
+  }
+
+  return JNI_TRUE;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canDiscoverMethod(
+    JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
+  ScopedUtfChars utf_name(env, name);
+  jmethodID method = is_static ? env->GetStaticMethodID(klass, utf_name.c_str(), "()I")
+                               : env->GetMethodID(klass, utf_name.c_str(), "()I");
+  if (method == NULL) {
+    env->ExceptionClear();
+    return JNI_FALSE;
+  }
+
+  return JNI_TRUE;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeMethodA(
+    JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
+  ScopedUtfChars utf_name(env, name);
+  jmethodID method = is_static ? env->GetStaticMethodID(klass, utf_name.c_str(), "()I")
+                               : env->GetMethodID(klass, utf_name.c_str(), "()I");
+  if (method == NULL) {
+    env->ExceptionClear();
+    return JNI_FALSE;
+  }
+
+  if (is_static) {
+    env->CallStaticIntMethodA(klass, method, nullptr);
+  } else {
+    jobject obj = NewInstance(env, klass);
+    if (obj == NULL) {
+      env->ExceptionDescribe();
+      env->ExceptionClear();
+      return JNI_FALSE;
+    }
+    env->CallIntMethodA(obj, method, nullptr);
+  }
+
+  if (env->ExceptionOccurred()) {
+    env->ExceptionDescribe();
+    env->ExceptionClear();
+    return JNI_FALSE;
+  }
+
+  return JNI_TRUE;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeMethodV(
+    JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
+  ScopedUtfChars utf_name(env, name);
+  jmethodID method = is_static ? env->GetStaticMethodID(klass, utf_name.c_str(), "()I")
+                               : env->GetMethodID(klass, utf_name.c_str(), "()I");
+  if (method == NULL) {
+    env->ExceptionClear();
+    return JNI_FALSE;
+  }
+
+  if (is_static) {
+    env->CallStaticIntMethod(klass, method);
+  } else {
+    jobject obj = NewInstance(env, klass);
+    if (obj == NULL) {
+      env->ExceptionDescribe();
+      env->ExceptionClear();
+      return JNI_FALSE;
+    }
+    env->CallIntMethod(obj, method);
+  }
+
+  if (env->ExceptionOccurred()) {
+    env->ExceptionDescribe();
+    env->ExceptionClear();
+    return JNI_FALSE;
+  }
+
+  return JNI_TRUE;
+}
+
+static constexpr size_t kConstructorSignatureLength = 5;  // e.g. (IZ)V
+static constexpr size_t kNumConstructorArgs = kConstructorSignatureLength - 3;
+
+extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canDiscoverConstructor(
+    JNIEnv* env, jclass, jclass klass, jstring args) {
+  ScopedUtfChars utf_args(env, args);
+  jmethodID constructor = env->GetMethodID(klass, "<init>", utf_args.c_str());
+  if (constructor == NULL) {
+    env->ExceptionClear();
+    return JNI_FALSE;
+  }
+
+  return JNI_TRUE;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeConstructorA(
+    JNIEnv* env, jclass, jclass klass, jstring args) {
+  ScopedUtfChars utf_args(env, args);
+  jmethodID constructor = env->GetMethodID(klass, "<init>", utf_args.c_str());
+  if (constructor == NULL) {
+    env->ExceptionClear();
+    return JNI_FALSE;
+  }
+
+  // CheckJNI won't allow out-of-range values, so just zero everything.
+  CHECK_EQ(strlen(utf_args.c_str()), kConstructorSignatureLength);
+  size_t initargs_size = sizeof(jvalue) * kNumConstructorArgs;
+  jvalue *initargs = reinterpret_cast<jvalue*>(alloca(initargs_size));
+  memset(initargs, 0, initargs_size);
+
+  env->NewObjectA(klass, constructor, initargs);
+  if (env->ExceptionOccurred()) {
+    env->ExceptionDescribe();
+    env->ExceptionClear();
+    return JNI_FALSE;
+  }
+
+  return JNI_TRUE;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeConstructorV(
+    JNIEnv* env, jclass, jclass klass, jstring args) {
+  ScopedUtfChars utf_args(env, args);
+  jmethodID constructor = env->GetMethodID(klass, "<init>", utf_args.c_str());
+  if (constructor == NULL) {
+    env->ExceptionClear();
+    return JNI_FALSE;
+  }
+
+  // CheckJNI won't allow out-of-range values, so just zero everything.
+  CHECK_EQ(strlen(utf_args.c_str()), kConstructorSignatureLength);
+  size_t initargs_size = sizeof(jvalue) * kNumConstructorArgs;
+  jvalue *initargs = reinterpret_cast<jvalue*>(alloca(initargs_size));
+  memset(initargs, 0, initargs_size);
+
+  static_assert(kNumConstructorArgs == 2, "Change the varargs below if you change the constant");
+  env->NewObject(klass, constructor, initargs[0], initargs[1]);
+  if (env->ExceptionOccurred()) {
+    env->ExceptionDescribe();
+    env->ExceptionClear();
+    return JNI_FALSE;
+  }
+
+  return JNI_TRUE;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Reflection_getHiddenApiAccessFlags(JNIEnv*, jclass) {
+  return static_cast<jint>(kAccHiddenApiBits);
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_ChildClass_hasPendingWarning(JNIEnv*, jclass) {
+  return false;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_ChildClass_clearWarning(JNIEnv*, jclass) {
+  return;
+}
+
+}  // namespace Test674HiddenApi
+}  // namespace art
diff --git a/test/674-hiddenapi/info.txt b/test/674-hiddenapi/info.txt
new file mode 100644
index 0000000..25ac6ae
--- /dev/null
+++ b/test/674-hiddenapi/info.txt
@@ -0,0 +1,15 @@
+Test whether hidden API access flags are being enforced. The test is composed of
+two JARs. The first (parent) defines methods and fields and the second (child)
+tries to access them with reflection/JNI or link against them. Note that the
+first is compiled twice - once with and once without hidden access flags.
+
+The test then proceeds to exercise the following combinations of class loading:
+(a) Both parent and child dex loaded with PathClassLoader, parent's class loader
+    is the child's class loader's parent. Access flags should not be enforced as
+    the parent does not belong to boot class path.
+(b) Parent is appended to boot class path, child is loaded with PathClassLoader.
+    In this situation child should not be able to access hidden methods/fields
+    of the parent.
+(c) Both parent and child are appended to boot class path. Restrictions should
+    not apply as hidden APIs are accessible within the boundaries of the boot
+    class path.
diff --git a/test/674-hiddenapi/src-art/Main.java b/test/674-hiddenapi/src-art/Main.java
new file mode 100644
index 0000000..56a644f
--- /dev/null
+++ b/test/674-hiddenapi/src-art/Main.java
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+
+import dalvik.system.InMemoryDexClassLoader;
+import dalvik.system.PathClassLoader;
+import java.io.File;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[0]);
+    prepareNativeLibFileName(args[0]);
+
+    // Run test with both parent and child dex files loaded with class loaders.
+    // The expectation is that hidden members in parent should be visible to
+    // the child.
+    doTest(false, false);
+    doUnloading();
+
+    // Now append parent dex file to boot class path and run again. This time
+    // the child dex file should not be able to access private APIs of the parent.
+    appendToBootClassLoader(DEX_PARENT_BOOT);
+    doTest(true, false);
+    doUnloading();
+
+    // And finally append to child to boot class path as well. With both in the
+    // boot class path, access should be granted.
+    appendToBootClassLoader(DEX_CHILD);
+    doTest(true, true);
+    doUnloading();
+  }
+
+  private static void doTest(boolean parentInBoot, boolean childInBoot) throws Exception {
+    // Load parent dex if it is not in boot class path.
+    ClassLoader parentLoader = null;
+    if (parentInBoot) {
+      parentLoader = BOOT_CLASS_LOADER;
+    } else {
+      parentLoader = new PathClassLoader(DEX_PARENT, ClassLoader.getSystemClassLoader());
+    }
+
+    // Load child dex if it is not in boot class path.
+    ClassLoader childLoader = null;
+    if (childInBoot) {
+      if (parentLoader != BOOT_CLASS_LOADER) {
+        throw new IllegalStateException(
+            "DeclaringClass must be in parent class loader of CallingClass");
+      }
+      childLoader = BOOT_CLASS_LOADER;
+    } else {
+      childLoader = new InMemoryDexClassLoader(readDexFile(DEX_CHILD), parentLoader);
+    }
+
+    // Create a unique copy of the native library. Each shared library can only
+    // be loaded once, but for some reason even classes from a class loader
+    // cannot register their native methods against symbols in a shared library
+    // loaded by their parent class loader.
+    String nativeLibCopy = createNativeLibCopy(parentInBoot, childInBoot);
+
+    // Invoke ChildClass.runTest
+    Class.forName("ChildClass", true, childLoader)
+        .getDeclaredMethod("runTest", String.class, Boolean.TYPE, Boolean.TYPE)
+            .invoke(null, nativeLibCopy, parentInBoot, childInBoot);
+  }
+
+  // Routine which tries to figure out the absolute path of our native library.
+  private static void prepareNativeLibFileName(String arg) throws Exception {
+    String libName = System.mapLibraryName(arg);
+    Method libPathsMethod = Runtime.class.getDeclaredMethod("getLibPaths");
+    libPathsMethod.setAccessible(true);
+    String[] libPaths = (String[]) libPathsMethod.invoke(Runtime.getRuntime());
+    nativeLibFileName = null;
+    for (String p : libPaths) {
+      String candidate = p + libName;
+      if (new File(candidate).exists()) {
+        nativeLibFileName = candidate;
+        break;
+      }
+    }
+    if (nativeLibFileName == null) {
+      throw new IllegalStateException("Didn't find " + libName + " in " +
+          Arrays.toString(libPaths));
+    }
+  }
+
+  // Helper to read dex file into memory.
+  private static ByteBuffer readDexFile(String jarFileName) throws Exception {
+    ZipFile zip = new ZipFile(new File(jarFileName));
+    ZipEntry entry = zip.getEntry("classes.dex");
+    InputStream is = zip.getInputStream(entry);
+    int offset = 0;
+    int size = (int) entry.getSize();
+    ByteBuffer buffer = ByteBuffer.allocate(size);
+    while (is.available() > 0) {
+      is.read(buffer.array(), offset, size - offset);
+    }
+    is.close();
+    zip.close();
+    return buffer;
+  }
+
+  // Copy native library to a new file with a unique name so it does not conflict
+  // with other loaded instance of the same binary file.
+  private static String createNativeLibCopy(boolean parentInBoot, boolean childInBoot)
+      throws Exception {
+    String tempFileName = System.mapLibraryName(
+        "hiddenapitest_" + (parentInBoot ? "1" : "0") + (childInBoot ? "1" : "0"));
+    File tempFile = new File(System.getenv("DEX_LOCATION"), tempFileName);
+    Files.copy(new File(nativeLibFileName).toPath(), tempFile.toPath());
+    return tempFile.getAbsolutePath();
+  }
+
+  private static void doUnloading() {
+    // Do multiple GCs to prevent rare flakiness if some other thread is keeping the
+    // classloader live.
+    for (int i = 0; i < 5; ++i) {
+       Runtime.getRuntime().gc();
+    }
+  }
+
+  private static String nativeLibFileName;
+
+  private static final String DEX_PARENT =
+      new File(System.getenv("DEX_LOCATION"), "674-hiddenapi.jar").getAbsolutePath();
+  private static final String DEX_PARENT_BOOT =
+      new File(new File(System.getenv("DEX_LOCATION"), "res"), "boot.jar").getAbsolutePath();
+  private static final String DEX_CHILD =
+      new File(System.getenv("DEX_LOCATION"), "674-hiddenapi-ex.jar").getAbsolutePath();
+
+  private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader();
+
+  private static native void appendToBootClassLoader(String dexPath);
+}
diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java
new file mode 100644
index 0000000..af615bf
--- /dev/null
+++ b/test/674-hiddenapi/src-ex/ChildClass.java
@@ -0,0 +1,429 @@
+/*
+ * 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.
+ */
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeVisitor;
+
+public class ChildClass {
+  enum PrimitiveType {
+    TInteger('I', Integer.TYPE, Integer.valueOf(0)),
+    TLong('J', Long.TYPE, Long.valueOf(0)),
+    TFloat('F', Float.TYPE, Float.valueOf(0)),
+    TDouble('D', Double.TYPE, Double.valueOf(0)),
+    TBoolean('Z', Boolean.TYPE, Boolean.valueOf(false)),
+    TByte('B', Byte.TYPE, Byte.valueOf((byte) 0)),
+    TShort('S', Short.TYPE, Short.valueOf((short) 0)),
+    TCharacter('C', Character.TYPE, Character.valueOf('0'));
+
+    PrimitiveType(char shorty, Class klass, Object value) {
+      mShorty = shorty;
+      mClass = klass;
+      mDefaultValue = value;
+    }
+
+    public char mShorty;
+    public Class mClass;
+    public Object mDefaultValue;
+  }
+
+  enum Hiddenness {
+    Whitelist(PrimitiveType.TShort),
+    LightGreylist(PrimitiveType.TBoolean),
+    DarkGreylist(PrimitiveType.TByte),
+    Blacklist(PrimitiveType.TCharacter);
+
+    Hiddenness(PrimitiveType type) { mAssociatedType = type; }
+    public PrimitiveType mAssociatedType;
+  }
+
+  enum Visibility {
+    Public(PrimitiveType.TInteger),
+    Package(PrimitiveType.TFloat),
+    Protected(PrimitiveType.TLong),
+    Private(PrimitiveType.TDouble);
+
+    Visibility(PrimitiveType type) { mAssociatedType = type; }
+    public PrimitiveType mAssociatedType;
+  }
+
+  enum Behaviour {
+    Granted,
+    Warning,
+    Denied,
+  }
+
+  private static final boolean booleanValues[] = new boolean[] { false, true };
+
+  public static void runTest(String libFileName, boolean expectedParentInBoot,
+      boolean expectedChildInBoot) throws Exception {
+    System.load(libFileName);
+
+    // Check expectations about loading into boot class path.
+    isParentInBoot = (ParentClass.class.getClassLoader().getParent() == null);
+    if (isParentInBoot != expectedParentInBoot) {
+      throw new RuntimeException("Expected ParentClass " +
+                                 (expectedParentInBoot ? "" : "not ") + "in boot class path");
+    }
+    isChildInBoot = (ChildClass.class.getClassLoader().getParent() == null);
+    if (isChildInBoot != expectedChildInBoot) {
+      throw new RuntimeException("Expected ChildClass " + (expectedChildInBoot ? "" : "not ") +
+                                 "in boot class path");
+    }
+
+    // Run meaningful combinations of access flags.
+    for (Hiddenness hiddenness : Hiddenness.values()) {
+      final Behaviour expected = Behaviour.Granted;
+
+      for (boolean isStatic : booleanValues) {
+        String suffix = (isStatic ? "Static" : "") + hiddenness.name();
+
+        for (Visibility visibility : Visibility.values()) {
+          // Test reflection and JNI on methods and fields
+          for (Class klass : new Class<?>[] { ParentClass.class, ParentInterface.class }) {
+            String baseName = visibility.name() + suffix;
+            checkField(klass, "field" + baseName, isStatic, visibility, expected);
+            checkMethod(klass, "method" + baseName, isStatic, visibility, expected);
+          }
+
+          // Check whether one can use a class constructor.
+          checkConstructor(ParentClass.class, visibility, hiddenness, expected);
+
+          // Check whether you can use an interface default method.
+          String name = "method" + visibility.name() + "Default" + hiddenness.name();
+          checkMethod(ParentInterface.class, name, /*isStatic*/ false, visibility, expected);
+        }
+
+        // Test whether static linking succeeds.
+        checkLinking("LinkFieldGet" + suffix, /*takesParameter*/ false, expected);
+        checkLinking("LinkFieldSet" + suffix, /*takesParameter*/ true, expected);
+        checkLinking("LinkMethod" + suffix, /*takesParameter*/ false, expected);
+      }
+
+      // Check whether Class.newInstance succeeds.
+      checkNullaryConstructor(Class.forName("NullaryConstructor" + hiddenness.name()), expected);
+    }
+  }
+
+  private static void checkField(Class<?> klass, String name, boolean isStatic,
+      Visibility visibility, Behaviour behaviour) throws Exception {
+
+    boolean isPublic = (visibility == Visibility.Public);
+    boolean canDiscover = (behaviour != Behaviour.Denied);
+    boolean setsWarning = (behaviour == Behaviour.Warning);
+
+    if (klass.isInterface() && (!isStatic || !isPublic)) {
+      // Interfaces only have public static fields.
+      return;
+    }
+
+    // Test discovery with reflection.
+
+    if (Reflection.canDiscoverWithGetDeclaredField(klass, name) != canDiscover) {
+      throwDiscoveryException(klass, name, true, "getDeclaredField()", canDiscover);
+    }
+
+    if (Reflection.canDiscoverWithGetDeclaredFields(klass, name) != canDiscover) {
+      throwDiscoveryException(klass, name, true, "getDeclaredFields()", canDiscover);
+    }
+
+    if (Reflection.canDiscoverWithGetField(klass, name) != (canDiscover && isPublic)) {
+      throwDiscoveryException(klass, name, true, "getField()", (canDiscover && isPublic));
+    }
+
+    if (Reflection.canDiscoverWithGetFields(klass, name) != (canDiscover && isPublic)) {
+      throwDiscoveryException(klass, name, true, "getFields()", (canDiscover && isPublic));
+    }
+
+    // Test discovery with JNI.
+
+    if (JNI.canDiscoverField(klass, name, isStatic) != canDiscover) {
+      throwDiscoveryException(klass, name, true, "JNI", canDiscover);
+    }
+
+    // Finish here if we could not discover the field.
+
+    if (!canDiscover) {
+      return;
+    }
+
+    // Test that modifiers are unaffected.
+
+    if (Reflection.canObserveFieldHiddenAccessFlags(klass, name)) {
+      throwModifiersException(klass, name, true);
+    }
+
+    // Test getters and setters when meaningful.
+
+    clearWarning();
+    if (!Reflection.canGetField(klass, name)) {
+      throwAccessException(klass, name, true, "Field.getInt()");
+    }
+    if (hasPendingWarning() != setsWarning) {
+      throwWarningException(klass, name, true, "Field.getInt()", setsWarning);
+    }
+
+    clearWarning();
+    if (!Reflection.canSetField(klass, name)) {
+      throwAccessException(klass, name, true, "Field.setInt()");
+    }
+    if (hasPendingWarning() != setsWarning) {
+      throwWarningException(klass, name, true, "Field.setInt()", setsWarning);
+    }
+
+    clearWarning();
+    if (!JNI.canGetField(klass, name, isStatic)) {
+      throwAccessException(klass, name, true, "getIntField");
+    }
+    if (hasPendingWarning() != setsWarning) {
+      throwWarningException(klass, name, true, "getIntField", setsWarning);
+    }
+
+    clearWarning();
+    if (!JNI.canSetField(klass, name, isStatic)) {
+      throwAccessException(klass, name, true, "setIntField");
+    }
+    if (hasPendingWarning() != setsWarning) {
+      throwWarningException(klass, name, true, "setIntField", setsWarning);
+    }
+  }
+
+  private static void checkMethod(Class<?> klass, String name, boolean isStatic,
+      Visibility visibility, Behaviour behaviour) throws Exception {
+
+    boolean isPublic = (visibility == Visibility.Public);
+    if (klass.isInterface() && !isPublic) {
+      // All interface members are public.
+      return;
+    }
+
+    boolean canDiscover = (behaviour != Behaviour.Denied);
+    boolean setsWarning = (behaviour == Behaviour.Warning);
+
+    // Test discovery with reflection.
+
+    if (Reflection.canDiscoverWithGetDeclaredMethod(klass, name) != canDiscover) {
+      throwDiscoveryException(klass, name, false, "getDeclaredMethod()", canDiscover);
+    }
+
+    if (Reflection.canDiscoverWithGetDeclaredMethods(klass, name) != canDiscover) {
+      throwDiscoveryException(klass, name, false, "getDeclaredMethods()", canDiscover);
+    }
+
+    if (Reflection.canDiscoverWithGetMethod(klass, name) != (canDiscover && isPublic)) {
+      throwDiscoveryException(klass, name, false, "getMethod()", (canDiscover && isPublic));
+    }
+
+    if (Reflection.canDiscoverWithGetMethods(klass, name) != (canDiscover && isPublic)) {
+      throwDiscoveryException(klass, name, false, "getMethods()", (canDiscover && isPublic));
+    }
+
+    // Test discovery with JNI.
+
+    if (JNI.canDiscoverMethod(klass, name, isStatic) != canDiscover) {
+      throwDiscoveryException(klass, name, false, "JNI", canDiscover);
+    }
+
+    // Finish here if we could not discover the field.
+
+    if (!canDiscover) {
+      return;
+    }
+
+    // Test that modifiers are unaffected.
+
+    if (Reflection.canObserveMethodHiddenAccessFlags(klass, name)) {
+      throwModifiersException(klass, name, false);
+    }
+
+    // Test whether we can invoke the method. This skips non-static interface methods.
+
+    if (!klass.isInterface() || isStatic) {
+      clearWarning();
+      if (!Reflection.canInvokeMethod(klass, name)) {
+        throwAccessException(klass, name, false, "invoke()");
+      }
+      if (hasPendingWarning() != setsWarning) {
+        throwWarningException(klass, name, false, "invoke()", setsWarning);
+      }
+
+      clearWarning();
+      if (!JNI.canInvokeMethodA(klass, name, isStatic)) {
+        throwAccessException(klass, name, false, "CallMethodA");
+      }
+      if (hasPendingWarning() != setsWarning) {
+        throwWarningException(klass, name, false, "CallMethodA()", setsWarning);
+      }
+
+      clearWarning();
+      if (!JNI.canInvokeMethodV(klass, name, isStatic)) {
+        throwAccessException(klass, name, false, "CallMethodV");
+      }
+      if (hasPendingWarning() != setsWarning) {
+        throwWarningException(klass, name, false, "CallMethodV()", setsWarning);
+      }
+    }
+  }
+
+  private static void checkConstructor(Class<?> klass, Visibility visibility, Hiddenness hiddenness,
+      Behaviour behaviour) throws Exception {
+
+    boolean isPublic = (visibility == Visibility.Public);
+    String signature = "(" + visibility.mAssociatedType.mShorty +
+                             hiddenness.mAssociatedType.mShorty + ")V";
+    String fullName = "<init>" + signature;
+    Class<?> args[] = new Class[] { visibility.mAssociatedType.mClass,
+                                    hiddenness.mAssociatedType.mClass };
+    Object initargs[] = new Object[] { visibility.mAssociatedType.mDefaultValue,
+                                       hiddenness.mAssociatedType.mDefaultValue };
+
+    boolean canDiscover = (behaviour != Behaviour.Denied);
+    boolean setsWarning = (behaviour == Behaviour.Warning);
+
+    // Test discovery with reflection.
+
+    if (Reflection.canDiscoverWithGetDeclaredConstructor(klass, args) != canDiscover) {
+      throwDiscoveryException(klass, fullName, false, "getDeclaredConstructor()", canDiscover);
+    }
+
+    if (Reflection.canDiscoverWithGetDeclaredConstructors(klass, args) != canDiscover) {
+      throwDiscoveryException(klass, fullName, false, "getDeclaredConstructors()", canDiscover);
+    }
+
+    if (Reflection.canDiscoverWithGetConstructor(klass, args) != (canDiscover && isPublic)) {
+      throwDiscoveryException(
+          klass, fullName, false, "getConstructor()", (canDiscover && isPublic));
+    }
+
+    if (Reflection.canDiscoverWithGetConstructors(klass, args) != (canDiscover && isPublic)) {
+      throwDiscoveryException(
+          klass, fullName, false, "getConstructors()", (canDiscover && isPublic));
+    }
+
+    // Test discovery with JNI.
+
+    if (JNI.canDiscoverConstructor(klass, signature) != canDiscover) {
+      throwDiscoveryException(klass, fullName, false, "JNI", canDiscover);
+    }
+
+    // Finish here if we could not discover the field.
+
+    if (!canDiscover) {
+      return;
+    }
+
+    // Test whether we can invoke the constructor.
+
+    clearWarning();
+    if (!Reflection.canInvokeConstructor(klass, args, initargs)) {
+      throwAccessException(klass, fullName, false, "invoke()");
+    }
+    if (hasPendingWarning() != setsWarning) {
+      throwWarningException(klass, fullName, false, "invoke()", setsWarning);
+    }
+
+    clearWarning();
+    if (!JNI.canInvokeConstructorA(klass, signature)) {
+      throwAccessException(klass, fullName, false, "NewObjectA");
+    }
+    if (hasPendingWarning() != setsWarning) {
+      throwWarningException(klass, fullName, false, "NewObjectA", setsWarning);
+    }
+
+    clearWarning();
+    if (!JNI.canInvokeConstructorV(klass, signature)) {
+      throwAccessException(klass, fullName, false, "NewObjectV");
+    }
+    if (hasPendingWarning() != setsWarning) {
+      throwWarningException(klass, fullName, false, "NewObjectV", setsWarning);
+    }
+  }
+
+  private static void checkNullaryConstructor(Class<?> klass, Behaviour behaviour)
+      throws Exception {
+    boolean canAccess = (behaviour != Behaviour.Denied);
+    boolean setsWarning = (behaviour == Behaviour.Warning);
+
+    clearWarning();
+    if (Reflection.canUseNewInstance(klass) != canAccess) {
+      throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
+          "be able to construct " + klass.getName() + ". " +
+          "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+    }
+    if (canAccess && hasPendingWarning() != setsWarning) {
+      throwWarningException(klass, "nullary constructor", false, "newInstance", setsWarning);
+    }
+  }
+
+  private static void checkLinking(String className, boolean takesParameter, Behaviour behaviour)
+      throws Exception {
+    boolean canAccess = (behaviour != Behaviour.Denied);
+    boolean setsWarning = (behaviour == Behaviour.Warning);
+
+    clearWarning();
+    if (Linking.canAccess(className, takesParameter) != canAccess) {
+      throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
+          "be able to verify " + className + "." +
+          "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+    }
+    if (canAccess && hasPendingWarning() != setsWarning) {
+      throwWarningException(
+          Class.forName(className), "access", false, "static linking", setsWarning);
+    }
+  }
+
+  private static void throwDiscoveryException(Class<?> klass, String name, boolean isField,
+      String fn, boolean canAccess) {
+    throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() +
+        "." + name + " to " + (canAccess ? "" : "not ") + "be discoverable with " + fn + ". " +
+        "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+  }
+
+  private static void throwAccessException(Class<?> klass, String name, boolean isField,
+      String fn) {
+    throw new RuntimeException("Expected to be able to access " + (isField ? "field " : "method ") +
+        klass.getName() + "." + name + " using " + fn + ". " +
+        "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+  }
+
+  private static void throwWarningException(Class<?> klass, String name, boolean isField,
+      String fn, boolean setsWarning) {
+    throw new RuntimeException("Expected access to " + (isField ? "field " : "method ") +
+        klass.getName() + "." + name + " using " + fn + " to " + (setsWarning ? "" : "not ") +
+        "set the warning flag. " +
+        "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+  }
+
+  private static void throwModifiersException(Class<?> klass, String name, boolean isField) {
+    throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() +
+        "." + name + " to not expose hidden modifiers");
+  }
+
+  private static boolean isParentInBoot;
+  private static boolean isChildInBoot;
+
+  private static native boolean hasPendingWarning();
+  private static native void clearWarning();
+}
diff --git a/test/674-hiddenapi/src-ex/JNI.java b/test/674-hiddenapi/src-ex/JNI.java
new file mode 100644
index 0000000..5dfb296
--- /dev/null
+++ b/test/674-hiddenapi/src-ex/JNI.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+public class JNI {
+  public static native boolean canDiscoverField(Class<?> klass, String name, boolean isStatic);
+  public static native boolean canGetField(Class<?> klass, String name, boolean isStatic);
+  public static native boolean canSetField(Class<?> klass, String name, boolean isStatic);
+
+  public static native boolean canDiscoverMethod(Class<?> klass, String name, boolean isStatic);
+  public static native boolean canInvokeMethodA(Class<?> klass, String name, boolean isStatic);
+  public static native boolean canInvokeMethodV(Class<?> klass, String name, boolean isStatic);
+
+  public static native boolean canDiscoverConstructor(Class<?> klass, String signature);
+  public static native boolean canInvokeConstructorA(Class<?> klass, String signature);
+  public static native boolean canInvokeConstructorV(Class<?> klass, String signature);
+}
diff --git a/test/674-hiddenapi/src-ex/Linking.java b/test/674-hiddenapi/src-ex/Linking.java
new file mode 100644
index 0000000..c6735d8
--- /dev/null
+++ b/test/674-hiddenapi/src-ex/Linking.java
@@ -0,0 +1,193 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+
+public class Linking {
+  public static boolean canAccess(String className, boolean takesParameter) throws Exception {
+    try {
+      Class<?> c = Class.forName(className);
+      if (takesParameter) {
+        c.getDeclaredMethod("access", Integer.TYPE).invoke(null, 42);
+      } else {
+        c.getDeclaredMethod("access").invoke(null);
+      }
+      return true;
+    } catch (InvocationTargetException ex) {
+      if (ex.getCause() instanceof IllegalAccessError) {
+        return false;
+      } else {
+        throw ex;
+      }
+    }
+  }
+}
+
+// INSTANCE FIELD GET
+
+class LinkFieldGetWhitelist {
+  public static int access() {
+    return new ParentClass().fieldPublicWhitelist;
+  }
+}
+
+class LinkFieldGetLightGreylist {
+  public static int access() {
+    return new ParentClass().fieldPublicLightGreylist;
+  }
+}
+
+class LinkFieldGetDarkGreylist {
+  public static int access() {
+    return new ParentClass().fieldPublicDarkGreylist;
+  }
+}
+
+class LinkFieldGetBlacklist {
+  public static int access() {
+    return new ParentClass().fieldPublicBlacklist;
+  }
+}
+
+// INSTANCE FIELD SET
+
+class LinkFieldSetWhitelist {
+  public static void access(int x) {
+    new ParentClass().fieldPublicWhitelist = x;
+  }
+}
+
+class LinkFieldSetLightGreylist {
+  public static void access(int x) {
+    new ParentClass().fieldPublicLightGreylist = x;
+  }
+}
+
+class LinkFieldSetDarkGreylist {
+  public static void access(int x) {
+    new ParentClass().fieldPublicDarkGreylist = x;
+  }
+}
+
+class LinkFieldSetBlacklist {
+  public static void access(int x) {
+    new ParentClass().fieldPublicBlacklist = x;
+  }
+}
+
+// STATIC FIELD GET
+
+class LinkFieldGetStaticWhitelist {
+  public static int access() {
+    return ParentClass.fieldPublicStaticWhitelist;
+  }
+}
+
+class LinkFieldGetStaticLightGreylist {
+  public static int access() {
+    return ParentClass.fieldPublicStaticLightGreylist;
+  }
+}
+
+class LinkFieldGetStaticDarkGreylist {
+  public static int access() {
+    return ParentClass.fieldPublicStaticDarkGreylist;
+  }
+}
+
+class LinkFieldGetStaticBlacklist {
+  public static int access() {
+    return ParentClass.fieldPublicStaticBlacklist;
+  }
+}
+
+// STATIC FIELD SET
+
+class LinkFieldSetStaticWhitelist {
+  public static void access(int x) {
+    ParentClass.fieldPublicStaticWhitelist = x;
+  }
+}
+
+class LinkFieldSetStaticLightGreylist {
+  public static void access(int x) {
+    ParentClass.fieldPublicStaticLightGreylist = x;
+  }
+}
+
+class LinkFieldSetStaticDarkGreylist {
+  public static void access(int x) {
+    ParentClass.fieldPublicStaticDarkGreylist = x;
+  }
+}
+
+class LinkFieldSetStaticBlacklist {
+  public static void access(int x) {
+    ParentClass.fieldPublicStaticBlacklist = x;
+  }
+}
+
+// INVOKE INSTANCE METHOD
+
+class LinkMethodWhitelist {
+  public static int access() {
+    return new ParentClass().methodPublicWhitelist();
+  }
+}
+
+class LinkMethodLightGreylist {
+  public static int access() {
+    return new ParentClass().methodPublicLightGreylist();
+  }
+}
+
+class LinkMethodDarkGreylist {
+  public static int access() {
+    return new ParentClass().methodPublicDarkGreylist();
+  }
+}
+
+class LinkMethodBlacklist {
+  public static int access() {
+    return new ParentClass().methodPublicBlacklist();
+  }
+}
+
+// INVOKE STATIC METHOD
+
+class LinkMethodStaticWhitelist {
+  public static int access() {
+    return ParentClass.methodPublicStaticWhitelist();
+  }
+}
+
+class LinkMethodStaticLightGreylist {
+  public static int access() {
+    return ParentClass.methodPublicStaticLightGreylist();
+  }
+}
+
+class LinkMethodStaticDarkGreylist {
+  public static int access() {
+    return ParentClass.methodPublicStaticDarkGreylist();
+  }
+}
+
+class LinkMethodStaticBlacklist {
+  public static int access() {
+    return ParentClass.methodPublicStaticBlacklist();
+  }
+}
diff --git a/test/674-hiddenapi/src-ex/Reflection.java b/test/674-hiddenapi/src-ex/Reflection.java
new file mode 100644
index 0000000..3667e91
--- /dev/null
+++ b/test/674-hiddenapi/src-ex/Reflection.java
@@ -0,0 +1,205 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+
+public class Reflection {
+  public static boolean canDiscoverWithGetDeclaredField(Class<?> klass, String name) {
+    try {
+      klass.getDeclaredField(name);
+      return true;
+    } catch (NoSuchFieldException ex) {
+      return false;
+    }
+  }
+
+  public static boolean canDiscoverWithGetDeclaredFields(Class<?> klass, String name) {
+    for (Field f : klass.getDeclaredFields()) {
+      if (f.getName().equals(name)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public static boolean canDiscoverWithGetField(Class<?> klass, String name) {
+    try {
+      klass.getField(name);
+      return true;
+    } catch (NoSuchFieldException ex) {
+      return false;
+    }
+  }
+
+  public static boolean canDiscoverWithGetFields(Class<?> klass, String name) {
+    for (Field f : klass.getFields()) {
+      if (f.getName().equals(name)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public static boolean canGetField(Class<?> klass, String name) {
+    try {
+      Field f = klass.getDeclaredField(name);
+      f.setAccessible(true);
+      f.getInt(Modifier.isStatic(f.getModifiers()) ? null : klass.newInstance());
+      return true;
+    } catch (Exception ex) {
+      ex.printStackTrace();
+      return false;
+    }
+  }
+
+  public static boolean canSetField(Class<?> klass, String name) {
+    try {
+      Field f = klass.getDeclaredField(name);
+      f.setAccessible(true);
+      f.setInt(Modifier.isStatic(f.getModifiers()) ? null : klass.newInstance(), 42);
+      return true;
+    } catch (Exception ex) {
+      ex.printStackTrace();
+      return false;
+    }
+  }
+
+  public static boolean canDiscoverWithGetDeclaredMethod(Class<?> klass, String name) {
+    try {
+      klass.getDeclaredMethod(name);
+      return true;
+    } catch (NoSuchMethodException ex) {
+      return false;
+    }
+  }
+
+  public static boolean canDiscoverWithGetDeclaredMethods(Class<?> klass, String name) {
+    for (Method m : klass.getDeclaredMethods()) {
+      if (m.getName().equals(name)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public static boolean canDiscoverWithGetMethod(Class<?> klass, String name) {
+    try {
+      klass.getMethod(name);
+      return true;
+    } catch (NoSuchMethodException ex) {
+      return false;
+    }
+  }
+
+  public static boolean canDiscoverWithGetMethods(Class<?> klass, String name) {
+    for (Method m : klass.getMethods()) {
+      if (m.getName().equals(name)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public static boolean canInvokeMethod(Class<?> klass, String name) {
+    try {
+      Method m = klass.getDeclaredMethod(name);
+      m.setAccessible(true);
+      m.invoke(klass.isInterface() ? null : klass.newInstance());
+      return true;
+    } catch (Exception ex) {
+      ex.printStackTrace();
+      return false;
+    }
+  }
+
+  public static boolean canDiscoverWithGetDeclaredConstructor(Class<?> klass, Class<?> args[]) {
+    try {
+      klass.getDeclaredConstructor(args);
+      return true;
+    } catch (NoSuchMethodException ex) {
+      return false;
+    }
+  }
+
+  public static boolean canDiscoverWithGetDeclaredConstructors(Class<?> klass, Class<?> args[]) {
+    for (Constructor c : klass.getDeclaredConstructors()) {
+      if (Arrays.equals(c.getParameterTypes(), args)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public static boolean canDiscoverWithGetConstructor(Class<?> klass, Class<?> args[]) {
+    try {
+      klass.getConstructor(args);
+      return true;
+    } catch (NoSuchMethodException ex) {
+      return false;
+    }
+  }
+
+  public static boolean canDiscoverWithGetConstructors(Class<?> klass, Class<?> args[]) {
+    for (Constructor c : klass.getConstructors()) {
+      if (Arrays.equals(c.getParameterTypes(), args)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public static boolean canInvokeConstructor(Class<?> klass, Class<?> args[], Object[] initargs) {
+    try {
+      Constructor c = klass.getDeclaredConstructor(args);
+      c.setAccessible(true);
+      c.newInstance(initargs);
+      return true;
+    } catch (Exception ex) {
+      ex.printStackTrace();
+      return false;
+    }
+  }
+
+  public static boolean canUseNewInstance(Class<?> klass) throws IllegalAccessException {
+    try {
+      klass.newInstance();
+      return true;
+    } catch (InstantiationException ex) {
+      return false;
+    }
+  }
+
+  private static native int getHiddenApiAccessFlags();
+
+  public static boolean canObserveFieldHiddenAccessFlags(Class<?> klass, String name)
+      throws Exception {
+    return (klass.getDeclaredField(name).getModifiers() & getHiddenApiAccessFlags()) != 0;
+  }
+
+  public static boolean canObserveMethodHiddenAccessFlags(Class<?> klass, String name)
+      throws Exception {
+    return (klass.getDeclaredMethod(name).getModifiers() & getHiddenApiAccessFlags()) != 0;
+  }
+
+  public static boolean canObserveConstructorHiddenAccessFlags(Class<?> klass, Class<?> args[])
+      throws Exception {
+    return (klass.getConstructor(args).getModifiers() & getHiddenApiAccessFlags()) != 0;
+  }
+}
diff --git a/test/674-hiddenapi/src/NullaryConstructorBlacklist.java b/test/674-hiddenapi/src/NullaryConstructorBlacklist.java
new file mode 100644
index 0000000..5bf6278
--- /dev/null
+++ b/test/674-hiddenapi/src/NullaryConstructorBlacklist.java
@@ -0,0 +1,21 @@
+/*
+ * 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 NullaryConstructorBlacklist {
+  public NullaryConstructorBlacklist() { x = 22; }
+  public NullaryConstructorBlacklist(int y) { x = y; }
+  protected int x;
+}
diff --git a/test/674-hiddenapi/src/NullaryConstructorDarkGreylist.java b/test/674-hiddenapi/src/NullaryConstructorDarkGreylist.java
new file mode 100644
index 0000000..c25a767
--- /dev/null
+++ b/test/674-hiddenapi/src/NullaryConstructorDarkGreylist.java
@@ -0,0 +1,21 @@
+/*
+ * 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 NullaryConstructorDarkGreylist {
+  public NullaryConstructorDarkGreylist() { x = 22; }
+  public NullaryConstructorDarkGreylist(int y) { x = y; }
+  protected int x;
+}
diff --git a/test/674-hiddenapi/src/NullaryConstructorLightGreylist.java b/test/674-hiddenapi/src/NullaryConstructorLightGreylist.java
new file mode 100644
index 0000000..d5dac8b
--- /dev/null
+++ b/test/674-hiddenapi/src/NullaryConstructorLightGreylist.java
@@ -0,0 +1,21 @@
+/*
+ * 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 NullaryConstructorLightGreylist {
+  public NullaryConstructorLightGreylist() { x = 22; }
+  public NullaryConstructorLightGreylist(int y) { x = y; }
+  protected int x;
+}
diff --git a/test/674-hiddenapi/src/NullaryConstructorWhitelist.java b/test/674-hiddenapi/src/NullaryConstructorWhitelist.java
new file mode 100644
index 0000000..d101907
--- /dev/null
+++ b/test/674-hiddenapi/src/NullaryConstructorWhitelist.java
@@ -0,0 +1,21 @@
+/*
+ * 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 NullaryConstructorWhitelist {
+  public NullaryConstructorWhitelist() { x = 22; }
+  public NullaryConstructorWhitelist(int y) { x = y; }
+  protected int x;
+}
diff --git a/test/674-hiddenapi/src/ParentClass.java b/test/674-hiddenapi/src/ParentClass.java
new file mode 100644
index 0000000..edad02d
--- /dev/null
+++ b/test/674-hiddenapi/src/ParentClass.java
@@ -0,0 +1,133 @@
+/*
+ * 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 ParentClass {
+  public ParentClass() {}
+
+  // INSTANCE FIELD
+
+  public int fieldPublicWhitelist = 211;
+  int fieldPackageWhitelist = 212;
+  protected int fieldProtectedWhitelist = 213;
+  private int fieldPrivateWhitelist = 214;
+
+  public int fieldPublicLightGreylist = 221;
+  int fieldPackageLightGreylist = 222;
+  protected int fieldProtectedLightGreylist = 223;
+  private int fieldPrivateLightGreylist = 224;
+
+  public int fieldPublicDarkGreylist = 231;
+  int fieldPackageDarkGreylist = 232;
+  protected int fieldProtectedDarkGreylist = 233;
+  private int fieldPrivateDarkGreylist = 234;
+
+  public int fieldPublicBlacklist = 241;
+  int fieldPackageBlacklist = 242;
+  protected int fieldProtectedBlacklist = 243;
+  private int fieldPrivateBlacklist = 244;
+
+  // STATIC FIELD
+
+  public static int fieldPublicStaticWhitelist = 111;
+  static int fieldPackageStaticWhitelist = 112;
+  protected static int fieldProtectedStaticWhitelist = 113;
+  private static int fieldPrivateStaticWhitelist = 114;
+
+  public static int fieldPublicStaticLightGreylist = 121;
+  static int fieldPackageStaticLightGreylist = 122;
+  protected static int fieldProtectedStaticLightGreylist = 123;
+  private static int fieldPrivateStaticLightGreylist = 124;
+
+  public static int fieldPublicStaticDarkGreylist = 131;
+  static int fieldPackageStaticDarkGreylist = 132;
+  protected static int fieldProtectedStaticDarkGreylist = 133;
+  private static int fieldPrivateStaticDarkGreylist = 134;
+
+  public static int fieldPublicStaticBlacklist = 141;
+  static int fieldPackageStaticBlacklist = 142;
+  protected static int fieldProtectedStaticBlacklist = 143;
+  private static int fieldPrivateStaticBlacklist = 144;
+
+  // INSTANCE METHOD
+
+  public int methodPublicWhitelist() { return 411; }
+  int methodPackageWhitelist() { return 412; }
+  protected int methodProtectedWhitelist() { return 413; }
+  private int methodPrivateWhitelist() { return 414; }
+
+  public int methodPublicLightGreylist() { return 421; }
+  int methodPackageLightGreylist() { return 422; }
+  protected int methodProtectedLightGreylist() { return 423; }
+  private int methodPrivateLightGreylist() { return 424; }
+
+  public int methodPublicDarkGreylist() { return 431; }
+  int methodPackageDarkGreylist() { return 432; }
+  protected int methodProtectedDarkGreylist() { return 433; }
+  private int methodPrivateDarkGreylist() { return 434; }
+
+  public int methodPublicBlacklist() { return 441; }
+  int methodPackageBlacklist() { return 442; }
+  protected int methodProtectedBlacklist() { return 443; }
+  private int methodPrivateBlacklist() { return 444; }
+
+  // STATIC METHOD
+
+  public static int methodPublicStaticWhitelist() { return 311; }
+  static int methodPackageStaticWhitelist() { return 312; }
+  protected static int methodProtectedStaticWhitelist() { return 313; }
+  private static int methodPrivateStaticWhitelist() { return 314; }
+
+  public static int methodPublicStaticLightGreylist() { return 321; }
+  static int methodPackageStaticLightGreylist() { return 322; }
+  protected static int methodProtectedStaticLightGreylist() { return 323; }
+  private static int methodPrivateStaticLightGreylist() { return 324; }
+
+  public static int methodPublicStaticDarkGreylist() { return 331; }
+  static int methodPackageStaticDarkGreylist() { return 332; }
+  protected static int methodProtectedStaticDarkGreylist() { return 333; }
+  private static int methodPrivateStaticDarkGreylist() { return 334; }
+
+  public static int methodPublicStaticBlacklist() { return 341; }
+  static int methodPackageStaticBlacklist() { return 342; }
+  protected static int methodProtectedStaticBlacklist() { return 343; }
+  private static int methodPrivateStaticBlacklist() { return 344; }
+
+  // CONSTRUCTOR
+
+  // Whitelist
+  public ParentClass(int x, short y) {}
+  ParentClass(float x, short y) {}
+  protected ParentClass(long x, short y) {}
+  private ParentClass(double x, short y) {}
+
+  // Light greylist
+  public ParentClass(int x, boolean y) {}
+  ParentClass(float x, boolean y) {}
+  protected ParentClass(long x, boolean y) {}
+  private ParentClass(double x, boolean y) {}
+
+  // Dark greylist
+  public ParentClass(int x, byte y) {}
+  ParentClass(float x, byte y) {}
+  protected ParentClass(long x, byte y) {}
+  private ParentClass(double x, byte y) {}
+
+  // Blacklist
+  public ParentClass(int x, char y) {}
+  ParentClass(float x, char y) {}
+  protected ParentClass(long x, char y) {}
+  private ParentClass(double x, char y) {}
+}
diff --git a/test/674-hiddenapi/src/ParentInterface.java b/test/674-hiddenapi/src/ParentInterface.java
new file mode 100644
index 0000000..e36fe0e
--- /dev/null
+++ b/test/674-hiddenapi/src/ParentInterface.java
@@ -0,0 +1,41 @@
+/*
+ * 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 interface ParentInterface {
+  // STATIC FIELD
+  static int fieldPublicStaticWhitelist = 11;
+  static int fieldPublicStaticLightGreylist = 12;
+  static int fieldPublicStaticDarkGreylist = 13;
+  static int fieldPublicStaticBlacklist = 14;
+
+  // INSTANCE METHOD
+  int methodPublicWhitelist();
+  int methodPublicBlacklist();
+  int methodPublicLightGreylist();
+  int methodPublicDarkGreylist();
+
+  // STATIC METHOD
+  static int methodPublicStaticWhitelist() { return 21; }
+  static int methodPublicStaticLightGreylist() { return 22; }
+  static int methodPublicStaticDarkGreylist() { return 23; }
+  static int methodPublicStaticBlacklist() { return 24; }
+
+  // DEFAULT METHOD
+  default int methodPublicDefaultWhitelist() { return 31; }
+  default int methodPublicDefaultLightGreylist() { return 32; }
+  default int methodPublicDefaultDarkGreylist() { return 33; }
+  default int methodPublicDefaultBlacklist() { return 34; }
+}
diff --git a/test/674-vdex-uncompress/build b/test/674-vdex-uncompress/build
new file mode 100755
index 0000000..7b1804d
--- /dev/null
+++ b/test/674-vdex-uncompress/build
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+# Uncompress and align the dex files so that dex2oat will not copy the dex
+# code to the .vdex file.
+./default-build "$@" --zip-compression-method store --zip-align 4
diff --git a/test/674-vdex-uncompress/expected.txt b/test/674-vdex-uncompress/expected.txt
new file mode 100644
index 0000000..d0f61f6
--- /dev/null
+++ b/test/674-vdex-uncompress/expected.txt
@@ -0,0 +1,2 @@
+In foo
+In foo
diff --git a/test/674-vdex-uncompress/info.txt b/test/674-vdex-uncompress/info.txt
new file mode 100644
index 0000000..6aa6f7b
--- /dev/null
+++ b/test/674-vdex-uncompress/info.txt
@@ -0,0 +1,2 @@
+Test that dex2oat can compile an APK that has uncompressed dex files,
+and where --input-vdex is passed.
diff --git a/test/674-vdex-uncompress/run b/test/674-vdex-uncompress/run
new file mode 100644
index 0000000..edf699f
--- /dev/null
+++ b/test/674-vdex-uncompress/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+exec ${RUN} -Xcompiler-option --compiler-filter=verify --vdex "${@}"
diff --git a/test/674-vdex-uncompress/src/Main.java b/test/674-vdex-uncompress/src/Main.java
new file mode 100644
index 0000000..0a25b56
--- /dev/null
+++ b/test/674-vdex-uncompress/src/Main.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+public class Main {
+  Main() {
+    // Will be quickened with RETURN_VOID_NO_BARRIER.
+  }
+
+  public static void main(String[] args) {
+    Main m = new Main();
+    Object o = m;
+    // The call and field accesses will be quickened.
+    m.foo(m.a);
+
+    // The checkcast will be quickened.
+    m.foo(((Main)o).a);
+  }
+
+  int a;
+  void foo(int a) {
+    System.out.println("In foo");
+  }
+}
+
diff --git a/test/710-varhandle-creation/src-art/Main.java b/test/710-varhandle-creation/src-art/Main.java
index 6d542bb..a737b5b 100644
--- a/test/710-varhandle-creation/src-art/Main.java
+++ b/test/710-varhandle-creation/src-art/Main.java
@@ -18,7 +18,6 @@
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-import dalvik.system.VMRuntime;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.VarHandle;
 import java.lang.invoke.VarHandle.AccessMode;
@@ -129,9 +128,6 @@
     static final VarHandle vbbd;
     static final VarHandle vbbo;
 
-    // Some test results vary depending on 32-bit vs 64-bit.
-    static final boolean IS_64_BIT = VMRuntime.getRuntime().is64Bit();
-
     static {
         try {
             vz = MethodHandles.lookup().findVarHandle(Main.class, "z", boolean.class);
@@ -1728,14 +1724,14 @@
         checkNotNull(vbaj);
         checkVarType(vbaj, long.class);
         checkCoordinateTypes(vbaj, "[class [B, int]");
-        checkVarHandleAccessMode(vbaj, VarHandle.AccessMode.GET, IS_64_BIT, "(byte[],int)long");
-        checkVarHandleAccessMode(vbaj, VarHandle.AccessMode.SET, IS_64_BIT, "(byte[],int,long)void");
-        checkVarHandleAccessMode(vbaj, VarHandle.AccessMode.GET_VOLATILE, IS_64_BIT, "(byte[],int)long");
-        checkVarHandleAccessMode(vbaj, VarHandle.AccessMode.SET_VOLATILE, IS_64_BIT, "(byte[],int,long)void");
-        checkVarHandleAccessMode(vbaj, VarHandle.AccessMode.GET_ACQUIRE, IS_64_BIT, "(byte[],int)long");
-        checkVarHandleAccessMode(vbaj, VarHandle.AccessMode.SET_RELEASE, IS_64_BIT, "(byte[],int,long)void");
-        checkVarHandleAccessMode(vbaj, VarHandle.AccessMode.GET_OPAQUE, IS_64_BIT, "(byte[],int)long");
-        checkVarHandleAccessMode(vbaj, VarHandle.AccessMode.SET_OPAQUE, IS_64_BIT, "(byte[],int,long)void");
+        checkVarHandleAccessMode(vbaj, VarHandle.AccessMode.GET, true, "(byte[],int)long");
+        checkVarHandleAccessMode(vbaj, VarHandle.AccessMode.SET, true, "(byte[],int,long)void");
+        checkVarHandleAccessMode(vbaj, VarHandle.AccessMode.GET_VOLATILE, true, "(byte[],int)long");
+        checkVarHandleAccessMode(vbaj, VarHandle.AccessMode.SET_VOLATILE, true, "(byte[],int,long)void");
+        checkVarHandleAccessMode(vbaj, VarHandle.AccessMode.GET_ACQUIRE, true, "(byte[],int)long");
+        checkVarHandleAccessMode(vbaj, VarHandle.AccessMode.SET_RELEASE, true, "(byte[],int,long)void");
+        checkVarHandleAccessMode(vbaj, VarHandle.AccessMode.GET_OPAQUE, true, "(byte[],int)long");
+        checkVarHandleAccessMode(vbaj, VarHandle.AccessMode.SET_OPAQUE, true, "(byte[],int,long)void");
         checkVarHandleAccessMode(vbaj, VarHandle.AccessMode.COMPARE_AND_SET, true, "(byte[],int,long,long)boolean");
         checkVarHandleAccessMode(vbaj, VarHandle.AccessMode.COMPARE_AND_EXCHANGE, true, "(byte[],int,long,long)long");
         checkVarHandleAccessMode(vbaj, VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE, true, "(byte[],int,long,long)long");
@@ -1800,14 +1796,14 @@
         checkNotNull(vbad);
         checkVarType(vbad, double.class);
         checkCoordinateTypes(vbad, "[class [B, int]");
-        checkVarHandleAccessMode(vbad, VarHandle.AccessMode.GET, IS_64_BIT, "(byte[],int)double");
-        checkVarHandleAccessMode(vbad, VarHandle.AccessMode.SET, IS_64_BIT, "(byte[],int,double)void");
-        checkVarHandleAccessMode(vbad, VarHandle.AccessMode.GET_VOLATILE, IS_64_BIT, "(byte[],int)double");
-        checkVarHandleAccessMode(vbad, VarHandle.AccessMode.SET_VOLATILE, IS_64_BIT, "(byte[],int,double)void");
-        checkVarHandleAccessMode(vbad, VarHandle.AccessMode.GET_ACQUIRE, IS_64_BIT, "(byte[],int)double");
-        checkVarHandleAccessMode(vbad, VarHandle.AccessMode.SET_RELEASE, IS_64_BIT, "(byte[],int,double)void");
-        checkVarHandleAccessMode(vbad, VarHandle.AccessMode.GET_OPAQUE, IS_64_BIT, "(byte[],int)double");
-        checkVarHandleAccessMode(vbad, VarHandle.AccessMode.SET_OPAQUE, IS_64_BIT, "(byte[],int,double)void");
+        checkVarHandleAccessMode(vbad, VarHandle.AccessMode.GET, true, "(byte[],int)double");
+        checkVarHandleAccessMode(vbad, VarHandle.AccessMode.SET, true, "(byte[],int,double)void");
+        checkVarHandleAccessMode(vbad, VarHandle.AccessMode.GET_VOLATILE, true, "(byte[],int)double");
+        checkVarHandleAccessMode(vbad, VarHandle.AccessMode.SET_VOLATILE, true, "(byte[],int,double)void");
+        checkVarHandleAccessMode(vbad, VarHandle.AccessMode.GET_ACQUIRE, true, "(byte[],int)double");
+        checkVarHandleAccessMode(vbad, VarHandle.AccessMode.SET_RELEASE, true, "(byte[],int,double)void");
+        checkVarHandleAccessMode(vbad, VarHandle.AccessMode.GET_OPAQUE, true, "(byte[],int)double");
+        checkVarHandleAccessMode(vbad, VarHandle.AccessMode.SET_OPAQUE, true, "(byte[],int,double)void");
         checkVarHandleAccessMode(vbad, VarHandle.AccessMode.COMPARE_AND_SET, true, "(byte[],int,double,double)boolean");
         checkVarHandleAccessMode(vbad, VarHandle.AccessMode.COMPARE_AND_EXCHANGE, true, "(byte[],int,double,double)double");
         checkVarHandleAccessMode(vbad, VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE, true, "(byte[],int,double,double)double");
@@ -1953,14 +1949,14 @@
         checkNotNull(vbbj);
         checkVarType(vbbj, long.class);
         checkCoordinateTypes(vbbj, "[class java.nio.ByteBuffer, int]");
-        checkVarHandleAccessMode(vbbj, VarHandle.AccessMode.GET, IS_64_BIT, "(ByteBuffer,int)long");
-        checkVarHandleAccessMode(vbbj, VarHandle.AccessMode.SET, IS_64_BIT, "(ByteBuffer,int,long)void");
-        checkVarHandleAccessMode(vbbj, VarHandle.AccessMode.GET_VOLATILE, IS_64_BIT, "(ByteBuffer,int)long");
-        checkVarHandleAccessMode(vbbj, VarHandle.AccessMode.SET_VOLATILE, IS_64_BIT, "(ByteBuffer,int,long)void");
-        checkVarHandleAccessMode(vbbj, VarHandle.AccessMode.GET_ACQUIRE, IS_64_BIT, "(ByteBuffer,int)long");
-        checkVarHandleAccessMode(vbbj, VarHandle.AccessMode.SET_RELEASE, IS_64_BIT, "(ByteBuffer,int,long)void");
-        checkVarHandleAccessMode(vbbj, VarHandle.AccessMode.GET_OPAQUE, IS_64_BIT, "(ByteBuffer,int)long");
-        checkVarHandleAccessMode(vbbj, VarHandle.AccessMode.SET_OPAQUE, IS_64_BIT, "(ByteBuffer,int,long)void");
+        checkVarHandleAccessMode(vbbj, VarHandle.AccessMode.GET, true, "(ByteBuffer,int)long");
+        checkVarHandleAccessMode(vbbj, VarHandle.AccessMode.SET, true, "(ByteBuffer,int,long)void");
+        checkVarHandleAccessMode(vbbj, VarHandle.AccessMode.GET_VOLATILE, true, "(ByteBuffer,int)long");
+        checkVarHandleAccessMode(vbbj, VarHandle.AccessMode.SET_VOLATILE, true, "(ByteBuffer,int,long)void");
+        checkVarHandleAccessMode(vbbj, VarHandle.AccessMode.GET_ACQUIRE, true, "(ByteBuffer,int)long");
+        checkVarHandleAccessMode(vbbj, VarHandle.AccessMode.SET_RELEASE, true, "(ByteBuffer,int,long)void");
+        checkVarHandleAccessMode(vbbj, VarHandle.AccessMode.GET_OPAQUE, true, "(ByteBuffer,int)long");
+        checkVarHandleAccessMode(vbbj, VarHandle.AccessMode.SET_OPAQUE, true, "(ByteBuffer,int,long)void");
         checkVarHandleAccessMode(vbbj, VarHandle.AccessMode.COMPARE_AND_SET, true, "(ByteBuffer,int,long,long)boolean");
         checkVarHandleAccessMode(vbbj, VarHandle.AccessMode.COMPARE_AND_EXCHANGE, true, "(ByteBuffer,int,long,long)long");
         checkVarHandleAccessMode(vbbj, VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE, true, "(ByteBuffer,int,long,long)long");
@@ -2025,14 +2021,14 @@
         checkNotNull(vbbd);
         checkVarType(vbbd, double.class);
         checkCoordinateTypes(vbbd, "[class java.nio.ByteBuffer, int]");
-        checkVarHandleAccessMode(vbbd, VarHandle.AccessMode.GET, IS_64_BIT, "(ByteBuffer,int)double");
-        checkVarHandleAccessMode(vbbd, VarHandle.AccessMode.SET, IS_64_BIT, "(ByteBuffer,int,double)void");
-        checkVarHandleAccessMode(vbbd, VarHandle.AccessMode.GET_VOLATILE, IS_64_BIT, "(ByteBuffer,int)double");
-        checkVarHandleAccessMode(vbbd, VarHandle.AccessMode.SET_VOLATILE, IS_64_BIT, "(ByteBuffer,int,double)void");
-        checkVarHandleAccessMode(vbbd, VarHandle.AccessMode.GET_ACQUIRE, IS_64_BIT, "(ByteBuffer,int)double");
-        checkVarHandleAccessMode(vbbd, VarHandle.AccessMode.SET_RELEASE, IS_64_BIT, "(ByteBuffer,int,double)void");
-        checkVarHandleAccessMode(vbbd, VarHandle.AccessMode.GET_OPAQUE, IS_64_BIT, "(ByteBuffer,int)double");
-        checkVarHandleAccessMode(vbbd, VarHandle.AccessMode.SET_OPAQUE, IS_64_BIT, "(ByteBuffer,int,double)void");
+        checkVarHandleAccessMode(vbbd, VarHandle.AccessMode.GET, true, "(ByteBuffer,int)double");
+        checkVarHandleAccessMode(vbbd, VarHandle.AccessMode.SET, true, "(ByteBuffer,int,double)void");
+        checkVarHandleAccessMode(vbbd, VarHandle.AccessMode.GET_VOLATILE, true, "(ByteBuffer,int)double");
+        checkVarHandleAccessMode(vbbd, VarHandle.AccessMode.SET_VOLATILE, true, "(ByteBuffer,int,double)void");
+        checkVarHandleAccessMode(vbbd, VarHandle.AccessMode.GET_ACQUIRE, true, "(ByteBuffer,int)double");
+        checkVarHandleAccessMode(vbbd, VarHandle.AccessMode.SET_RELEASE, true, "(ByteBuffer,int,double)void");
+        checkVarHandleAccessMode(vbbd, VarHandle.AccessMode.GET_OPAQUE, true, "(ByteBuffer,int)double");
+        checkVarHandleAccessMode(vbbd, VarHandle.AccessMode.SET_OPAQUE, true, "(ByteBuffer,int,double)void");
         checkVarHandleAccessMode(vbbd, VarHandle.AccessMode.COMPARE_AND_SET, true, "(ByteBuffer,int,double,double)boolean");
         checkVarHandleAccessMode(vbbd, VarHandle.AccessMode.COMPARE_AND_EXCHANGE, true, "(ByteBuffer,int,double,double)double");
         checkVarHandleAccessMode(vbbd, VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE, true, "(ByteBuffer,int,double,double)double");
diff --git a/test/Android.bp b/test/Android.bp
index 49a34a1..470a68f 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -389,6 +389,7 @@
         "661-oat-writer-layout/oat_writer_layout.cc",
         "664-aget-verifier/aget-verifier.cc",
         "667-jit-jni-stub/jit_jni_stub_test.cc",
+        "674-hiddenapi/hiddenapi.cc",
         "708-jit-cache-churn/jit.cc",
     ],
     shared_libs: [
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 2cada76..2df0cc6 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -20,6 +20,7 @@
 # Dependencies for actually running a run-test.
 TEST_ART_RUN_TEST_DEPENDENCIES := \
   $(HOST_OUT_EXECUTABLES)/dx \
+  $(HOST_OUT_EXECUTABLES)/hiddenapi \
   $(HOST_OUT_EXECUTABLES)/jasmin \
   $(HOST_OUT_EXECUTABLES)/smali \
   $(HOST_OUT_EXECUTABLES)/dexmerger \
diff --git a/test/etc/default-build b/test/etc/default-build
index 5c8257f..4dc2393 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -81,6 +81,17 @@
   HAS_SRC_DEX2OAT_UNRESOLVED=false
 fi
 
+if [ -f api-light-greylist.txt -o -f api-dark-greylist.txt -o -f api-blacklist.txt ]; then
+  HAS_HIDDENAPI_SPEC=true
+else
+  HAS_HIDDENAPI_SPEC=false
+fi
+
+# USE_HIDDENAPI=false run-test... will disable hiddenapi.
+if [ -z "${USE_HIDDENAPI}" ]; then
+  USE_HIDDENAPI=true
+fi
+
 # DESUGAR=false run-test... will disable desugar.
 if [[ "$DESUGAR" == false ]]; then
   USE_DESUGAR=false
@@ -321,6 +332,24 @@
   ${DXMERGER} "$dst_file" "${dex_files_to_merge[@]}"
 }
 
+function make_hiddenapi() {
+  local args=()
+  while [[ $# -gt 0 ]]; do
+    args+=("--dex=$1")
+    shift
+  done
+  if [ -f api-light-greylist.txt ]; then
+    args+=("--light-greylist=api-light-greylist.txt")
+  fi
+  if [ -f api-dark-greylist.txt ]; then
+    args+=("--dark-greylist=api-dark-greylist.txt")
+  fi
+  if [ -f api-blacklist.txt ]; then
+    args+=("--blacklist=api-blacklist.txt")
+  fi
+  ${HIDDENAPI} "${args[@]}"
+}
+
 # Print the directory name only if it exists.
 function maybe_dir() {
   local dirname="$1"
@@ -334,6 +363,13 @@
   exit 0
 fi
 
+# Helper function for a common test. Evaluate with $(has_mutlidex).
+function has_multidex() {
+  echo [ ${HAS_SRC_MULTIDEX} = "true" \
+         -o ${HAS_JASMIN_MULTIDEX} = "true" \
+         -o ${HAS_SMALI_MULTIDEX} = "true" ]
+}
+
 if [ ${HAS_SRC_DEX2OAT_UNRESOLVED} = "true" ]; then
   mkdir classes
   mkdir classes-ex
@@ -501,9 +537,18 @@
   fi
 fi
 
+# Apply hiddenapi on the dex files if the test has API list file(s).
+if [ ${NEED_DEX} = "true" -a ${USE_HIDDENAPI} = "true" -a ${HAS_HIDDENAPI_SPEC} = "true" ]; then
+  if $(has_multidex); then
+    make_hiddenapi classes.dex classes2.dex
+  else
+    make_hiddenapi classes.dex
+  fi
+fi
+
 # Create a single dex jar with two dex files for multidex.
 if [ ${NEED_DEX} = "true" ]; then
-  if [ ${HAS_SRC_MULTIDEX} = "true" ] || [ ${HAS_JASMIN_MULTIDEX} = "true" ] || [ ${HAS_SMALI_MULTIDEX} = "true" ]; then
+  if $(has_multidex); then
     zip $TEST_NAME.jar classes.dex classes2.dex
   else
     zip $TEST_NAME.jar classes.dex
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 41d976a..27bec3e 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -256,11 +256,6 @@
                         "testing deoptimizing at quick-to-interpreter bridge."]
     },
     {
-        "tests": "137-cfi",
-        "description": ["CFI unwinding expects managed frames"],
-        "variant": "interpreter"
-    },
-    {
         "tests": "906-iterate-heap",
         "description": ["Test 906 iterates the heap filtering with different",
                         "options. No instances should be created between those",
diff --git a/test/run-test b/test/run-test
index 75fe15c..a453f22 100755
--- a/test/run-test
+++ b/test/run-test
@@ -113,6 +113,12 @@
 fi
 export ZIPALIGN
 
+# If hiddenapi was not set by the environment variable, assume it is in
+# ANDROID_HOST_OUT.
+if [ -z "$HIDDENAPI" ]; then
+  export HIDDENAPI="${ANDROID_HOST_OUT}/bin/hiddenapi"
+fi
+
 info="info.txt"
 build="build"
 run="run"
diff --git a/test/ti-agent/common_helper.h b/test/ti-agent/common_helper.h
index fafa1af..e46abb6 100644
--- a/test/ti-agent/common_helper.h
+++ b/test/ti-agent/common_helper.h
@@ -22,7 +22,7 @@
 
 namespace art {
 
-// Taken from art/runtime/modifiers.h
+// Taken from art/runtime/dex/modifiers.h
 static constexpr uint32_t kAccStatic =       0x0008;  // field, method, ic
 
 jobject GetJavaField(jvmtiEnv* jvmti, JNIEnv* env, jclass field_klass, jfieldID f);
diff --git a/tools/cpp-define-generator/constant_class.def b/tools/cpp-define-generator/constant_class.def
index 4f1d875..1310103 100644
--- a/tools/cpp-define-generator/constant_class.def
+++ b/tools/cpp-define-generator/constant_class.def
@@ -15,8 +15,8 @@
  */
 
 #if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#include "modifiers.h"            // kAccClassIsFinalizable
 #include "base/bit_utils.h"       // MostSignificantBit
+#include "dex/modifiers.h"        // kAccClassIsFinalizable
 #endif
 
 #define DEFINE_FLAG_OFFSET(type_name, field_name, expr) \
diff --git a/tools/cpp-define-generator/constant_globals.def b/tools/cpp-define-generator/constant_globals.def
index 5018f52..539633e 100644
--- a/tools/cpp-define-generator/constant_globals.def
+++ b/tools/cpp-define-generator/constant_globals.def
@@ -18,8 +18,8 @@
 
 #if defined(DEFINE_INCLUDE_DEPENDENCIES)
 #include <atomic>            // std::memory_order_relaxed
+#include "dex/modifiers.h"
 #include "globals.h"         // art::kObjectAlignment
-#include "modifiers.h"
 #endif
 
 DEFINE_EXPR(STD_MEMORY_ORDER_RELAXED, int32_t, std::memory_order_relaxed)