Merge "Revert "Revert "Full-stack integrity: check vdex contents."""
diff --git a/compiler/compiler.h b/compiler/compiler.h
index b92bff6..a17e2b5 100644
--- a/compiler/compiler.h
+++ b/compiler/compiler.h
@@ -39,6 +39,12 @@
 class OatWriter;
 class Thread;
 
+enum class CopyOption {
+  kNever,
+  kAlways,
+  kOnlyIfCompressed
+};
+
 class Compiler {
  public:
   enum Kind {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 3732b17..1578656 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -457,7 +457,7 @@
   UsageError("  --deduplicate-code=true|false: enable|disable code deduplication. Deduplicated");
   UsageError("      code will have an arbitrary symbol tagged with [DEDUPED].");
   UsageError("");
-  UsageError("  --copying-dex-files=true|false: enable|disable copying the dex files into the");
+  UsageError("  --copy-dex-files=true|false: enable|disable copying the dex files into the");
   UsageError("      output vdex.");
   UsageError("");
   UsageError("  --compilation-reason=<string>: optional metadata specifying the reason for");
@@ -2921,8 +2921,9 @@
   // Whether the given input vdex is also the output.
   bool update_input_vdex_ = false;
 
-  // By default, copy the dex to the vdex file.
-  bool copy_dex_files_ = true;
+  // By default, copy the dex to the vdex file only if dex files are
+  // compressed in APK.
+  CopyOption copy_dex_files_ = CopyOption::kOnlyIfCompressed;
 
   // The reason for invoking the compiler.
   std::string compilation_reason_;
diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc
index 1ef100d..0d68f4f 100644
--- a/dex2oat/dex2oat_options.cc
+++ b/dex2oat/dex2oat_options.cc
@@ -234,9 +234,10 @@
       .Define("--force-determinism")
           .IntoKey(M::ForceDeterminism)
       .Define("--copy-dex-files=_")
-          .WithType<bool>()
-          .WithValueMap({{"true", false},
-                         {"false", false}})
+          .WithType<CopyOption>()
+          .WithValueMap({{"true", CopyOption::kOnlyIfCompressed},
+                         {"false", CopyOption::kNever},
+                         {"always", CopyOption::kAlways}})
           .IntoKey(M::CopyDexFiles)
       .Define("--classpath-dir=_")
           .WithType<std::string>()
diff --git a/dex2oat/dex2oat_options.def b/dex2oat/dex2oat_options.def
index a9d349b..01f9d94 100644
--- a/dex2oat/dex2oat_options.def
+++ b/dex2oat/dex2oat_options.def
@@ -74,7 +74,7 @@
 DEX2OAT_OPTIONS_KEY (Unit,                           DumpTiming)
 DEX2OAT_OPTIONS_KEY (Unit,                           DumpPasses)
 DEX2OAT_OPTIONS_KEY (Unit,                           DumpStats)
-DEX2OAT_OPTIONS_KEY (bool,                           CopyDexFiles)
+DEX2OAT_OPTIONS_KEY (CopyOption,                     CopyDexFiles)
 DEX2OAT_OPTIONS_KEY (Unit,                           AvoidStoringInvocation)
 DEX2OAT_OPTIONS_KEY (std::string,                    SwapFile)
 DEX2OAT_OPTIONS_KEY (int,                            SwapFileFd)
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 303c873..c6ce951 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -276,7 +276,7 @@
             &key_value_store,
             /* verify */ false,           // Dex files may be dex-to-dex-ed, don't verify.
             /* update_input_vdex */ false,
-            /* extract_dex_files */ true,
+            /* copy_dex_files */ CopyOption::kOnlyIfCompressed,
             &cur_opened_dex_files_maps,
             &cur_opened_dex_files);
         ASSERT_TRUE(dex_files_ok);
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index ff0729f..e22936c 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -643,7 +643,7 @@
     SafeMap<std::string, std::string>* key_value_store,
     bool verify,
     bool update_input_vdex,
-    bool copy_dex_files,
+    CopyOption copy_dex_files,
     /*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);
@@ -3330,13 +3330,13 @@
 bool OatWriter::WriteDexFiles(OutputStream* out,
                               File* file,
                               bool update_input_vdex,
-                              bool copy_dex_files) {
+                              CopyOption copy_dex_files) {
   TimingLogger::ScopedTiming split("Write Dex files", timings_);
 
   vdex_dex_files_offset_ = vdex_size_;
 
   // If extraction is enabled, only do it if not all the dex files are aligned and uncompressed.
-  if (copy_dex_files) {
+  if (copy_dex_files == CopyOption::kOnlyIfCompressed) {
     extract_dex_files_into_vdex_ = false;
     for (OatDexFile& oat_dex_file : oat_dex_files_) {
       if (!oat_dex_file.source_.IsZipEntry()) {
@@ -3349,7 +3349,10 @@
         break;
       }
     }
+  } else if (copy_dex_files == CopyOption::kAlways) {
+    extract_dex_files_into_vdex_ = true;
   } else {
+    DCHECK(copy_dex_files == CopyOption::kNever);
     extract_dex_files_into_vdex_ = false;
   }
 
diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h
index 6a7e1e4..2bc56c6 100644
--- a/dex2oat/linker/oat_writer.h
+++ b/dex2oat/linker/oat_writer.h
@@ -24,6 +24,7 @@
 
 #include "base/array_ref.h"
 #include "base/dchecked_vector.h"
+#include "compiler.h"
 #include "dex/compact_dex_level.h"
 #include "debug/debug_info.h"
 #include "linker/relative_patcher.h"  // For RelativePatcherTargetProvider.
@@ -168,7 +169,6 @@
   // This is generally the case, and should only be false for tests.
   // If `update_input_vdex` is true, then this method won't actually write the dex files,
   // and the compiler will just re-use the existing vdex file.
-  // If `copy_dex_files` is true, copy the input dex files into the vdex file.
   bool WriteAndOpenDexFiles(File* vdex_file,
                             OutputStream* oat_rodata,
                             InstructionSet instruction_set,
@@ -176,7 +176,7 @@
                             SafeMap<std::string, std::string>* key_value_store,
                             bool verify,
                             bool update_input_vdex,
-                            bool copy_dex_files,
+                            CopyOption copy_dex_files,
                             /*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);
@@ -288,7 +288,7 @@
   bool WriteDexFiles(OutputStream* out,
                      File* file,
                      bool update_input_vdex,
-                     bool copy_dex_files);
+                     CopyOption copy_dex_files);
   bool WriteDexFile(OutputStream* out,
                     File* file,
                     OatDexFile* oat_dex_file,
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index 6dd8e19..00b9abe 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -198,7 +198,7 @@
         &key_value_store,
         verify,
         /* update_input_vdex */ false,
-        compiler_driver_->GetCompilerOptions().GetCompilerFilter(),
+        CopyOption::kOnlyIfCompressed,
         &opened_dex_files_maps,
         &opened_dex_files)) {
       return false;
diff --git a/libdexfile/dex/dex_file_loader.cc b/libdexfile/dex/dex_file_loader.cc
index f0e54ea..758a2f0 100644
--- a/libdexfile/dex/dex_file_loader.cc
+++ b/libdexfile/dex/dex_file_loader.cc
@@ -204,10 +204,12 @@
 // All of the implementations here should be independent of the runtime.
 // TODO: implement all the virtual methods.
 
-bool DexFileLoader::GetMultiDexChecksums(const char* filename ATTRIBUTE_UNUSED,
-                                         std::vector<uint32_t>* checksums ATTRIBUTE_UNUSED,
-                                         std::string* error_msg,
-                                         int zip_fd ATTRIBUTE_UNUSED) const {
+bool DexFileLoader::GetMultiDexChecksums(
+    const char* filename ATTRIBUTE_UNUSED,
+    std::vector<uint32_t>* checksums ATTRIBUTE_UNUSED,
+    std::string* error_msg,
+    int zip_fd ATTRIBUTE_UNUSED,
+    bool* zip_file_only_contains_uncompress_dex ATTRIBUTE_UNUSED) const {
   *error_msg = "UNIMPLEMENTED";
   return false;
 }
diff --git a/libdexfile/dex/dex_file_loader.h b/libdexfile/dex/dex_file_loader.h
index 8d1cfae..28cdfc1 100644
--- a/libdexfile/dex/dex_file_loader.h
+++ b/libdexfile/dex/dex_file_loader.h
@@ -108,7 +108,8 @@
   virtual bool GetMultiDexChecksums(const char* filename,
                                     std::vector<uint32_t>* checksums,
                                     std::string* error_msg,
-                                    int zip_fd = -1) const;
+                                    int zip_fd = -1,
+                                    bool* zip_file_only_contains_uncompress_dex = nullptr) const;
 
   // Opens .dex file, backed by existing memory
   virtual std::unique_ptr<const DexFile> Open(const uint8_t* base,
diff --git a/runtime/dex/art_dex_file_loader.cc b/runtime/dex/art_dex_file_loader.cc
index 14386a3..c456764 100644
--- a/runtime/dex/art_dex_file_loader.cc
+++ b/runtime/dex/art_dex_file_loader.cc
@@ -85,7 +85,8 @@
 bool ArtDexFileLoader::GetMultiDexChecksums(const char* filename,
                                             std::vector<uint32_t>* checksums,
                                             std::string* error_msg,
-                                            int zip_fd) const {
+                                            int zip_fd,
+                                            bool* zip_file_only_contains_uncompressed_dex) const {
   CHECK(checksums != nullptr);
   uint32_t magic;
 
@@ -119,7 +120,17 @@
       return false;
     }
 
+    if (zip_file_only_contains_uncompressed_dex != nullptr) {
+      // Start by assuming everything is uncompressed.
+      *zip_file_only_contains_uncompressed_dex = true;
+    }
+
     do {
+      if (zip_file_only_contains_uncompressed_dex != nullptr) {
+        if (!(zip_entry->IsUncompressed() && zip_entry->IsAlignedToDexHeader())) {
+          *zip_file_only_contains_uncompressed_dex = false;
+        }
+      }
       checksums->push_back(zip_entry->GetCrc32());
       zip_entry_name = GetMultiDexClassesDexName(i++);
       zip_entry.reset(zip_archive->Find(zip_entry_name.c_str(), error_msg));
diff --git a/runtime/dex/art_dex_file_loader.h b/runtime/dex/art_dex_file_loader.h
index 3585381..7c7a59b 100644
--- a/runtime/dex/art_dex_file_loader.h
+++ b/runtime/dex/art_dex_file_loader.h
@@ -50,7 +50,8 @@
   bool GetMultiDexChecksums(const char* filename,
                             std::vector<uint32_t>* checksums,
                             std::string* error_msg,
-                            int zip_fd = -1) const OVERRIDE;
+                            int zip_fd = -1,
+                            bool* only_contains_uncompressed_dex = nullptr) const OVERRIDE;
 
   // Opens .dex file, backed by existing memory
   std::unique_ptr<const DexFile> Open(const uint8_t* base,
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 0170073..6bf05b7 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -515,6 +515,18 @@
     VLOG(oat) << "Image checksum test skipped for compiler filter " << current_compiler_filter;
   }
 
+  // zip_file_only_contains_uncompressed_dex_ is only set during fetching the dex checksums.
+  DCHECK(required_dex_checksums_attempted_);
+  if (only_load_system_executable_ &&
+      !LocationIsOnSystem(file.GetLocation().c_str()) &&
+      file.ContainsDexCode() &&
+      zip_file_only_contains_uncompressed_dex_) {
+    LOG(ERROR) << "Not loading "
+               << dex_location_
+               << ": oat file has dex code, but APK has uncompressed dex code";
+    return kOatDexOutOfDate;
+  }
+
   if (CompilerFilter::IsAotCompilationEnabled(current_compiler_filter)) {
     if (!file.IsPic()) {
       const ImageInfo* image_info = GetImageInfo();
@@ -879,7 +891,8 @@
     if (dex_file_loader.GetMultiDexChecksums(dex_location_.c_str(),
                                              &cached_required_dex_checksums_,
                                              &error_msg,
-                                             zip_fd_)) {
+                                             zip_fd_,
+                                             &zip_file_only_contains_uncompressed_dex_)) {
       required_dex_checksums_found_ = true;
       has_original_dex_files_ = true;
     } else {
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index a184807..6da49a9 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -508,6 +508,9 @@
 
   // Whether only oat files on /system are loaded executable.
   const bool only_load_system_executable_ = false;
+  // Whether the potential zip file only contains uncompressed dex.
+  // Will be set during GetRequiredDexChecksums.
+  bool zip_file_only_contains_uncompressed_dex_ = true;
 
   // Cached value of the required dex checksums.
   // This should be accessed only by the GetRequiredDexChecksums() method.
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index c7f73d1..c61ecc8 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -334,6 +334,8 @@
           .IntoKey(M::HiddenApiChecks)
       .Define("-Xuse-stderr-logger")
           .IntoKey(M::UseStderrLogger)
+      .Define("-Xonly-use-system-oat-files")
+          .IntoKey(M::OnlyUseSystemOatFiles)
       .Ignore({
           "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
           "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 2074f1e..4442fc6 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1593,6 +1593,11 @@
 
   VLOG(startup) << "Runtime::Init exiting";
 
+  // Set OnlyUseSystemOatFiles only after boot classpath has been set up.
+  if (runtime_options.Exists(Opt::OnlyUseSystemOatFiles)) {
+    oat_file_manager_->SetOnlyUseSystemOatFiles();
+  }
+
   return true;
 }
 
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index dba648e..4121ad6 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -152,4 +152,6 @@
 RUNTIME_OPTIONS_KEY (unsigned int,        GlobalRefAllocStackTraceLimit,  0)  // 0 = off
 RUNTIME_OPTIONS_KEY (Unit,                UseStderrLogger)
 
+RUNTIME_OPTIONS_KEY (Unit,                OnlyUseSystemOatFiles)
+
 #undef RUNTIME_OPTIONS_KEY
diff --git a/test/677-fsi/build b/test/677-fsi/build
new file mode 100755
index 0000000..b90b408
--- /dev/null
+++ b/test/677-fsi/build
@@ -0,0 +1,17 @@
+#!/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.
+
+./default-build "$@" --zip-compression-method store --zip-align 4
diff --git a/test/677-fsi/check b/test/677-fsi/check
new file mode 100644
index 0000000..a84cf65
--- /dev/null
+++ b/test/677-fsi/check
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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 part of message containing the JDWP log (only on target)
+sed -s '/^.*JDWP support\./d' "$2" > "$2.tmp"
+
+# Remove part of message containing filename.
+sed -s 's/^.*: //' "$2.tmp" > "$2.tmp2"
+
+diff --strip-trailing-cr -q "$1" "$2.tmp2" >/dev/null
diff --git a/test/677-fsi/expected.txt b/test/677-fsi/expected.txt
new file mode 100644
index 0000000..c7fb8fe
--- /dev/null
+++ b/test/677-fsi/expected.txt
@@ -0,0 +1,3 @@
+oat file has dex code, but APK has uncompressed dex code
+oat file has dex code, but APK has uncompressed dex code
+Hello World
diff --git a/test/677-fsi/info.txt b/test/677-fsi/info.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/677-fsi/info.txt
diff --git a/test/677-fsi/run b/test/677-fsi/run
new file mode 100644
index 0000000..30d925e
--- /dev/null
+++ b/test/677-fsi/run
@@ -0,0 +1,19 @@
+#!/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.
+
+# Redirect logger to stderr, as the test relies on error
+# messages being printed there.
+exec ${RUN} $@ -Xcompiler-option --copy-dex-files=always --runtime-option -Xonly-use-system-oat-files --runtime-option -Xuse-stderr-logger
diff --git a/test/677-fsi/src/Main.java b/test/677-fsi/src/Main.java
new file mode 100644
index 0000000..834075f
--- /dev/null
+++ b/test/677-fsi/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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 {
+  public static void main(String[] args) {
+    System.out.println("Hello World");
+  }
+}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index cc3d200..8426a14 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -970,7 +970,10 @@
         "variant": "jvm",
         "bug": "b/73888836",
         "description": ["Failing on JVM. Needs further investigating."]
+    },
+    {
+        "tests": "677-fsi",
+        "variant": "no-dex2oat | no-image | no-prebuild | relocate-npatchoat",
+        "description": ["Test requires a successful dex2oat invocation"]
     }
-
-
 ]