/*
 * 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 "base/file_utils.h"
#include "class_loader_utils.h"
#include "jni.h"
#include "nativehelper/scoped_utf_chars.h"
#include "oat_file_assistant.h"
#include "oat_file_manager.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"

namespace art {
namespace Test692VdexInmemLoader {

extern "C" JNIEXPORT void JNICALL Java_Main_waitForVerifier(JNIEnv*, jclass) {
  Runtime::Current()->GetOatFileManager().WaitForBackgroundVerificationTasks();
}

extern "C" JNIEXPORT void JNICALL Java_Main_setProcessDataDir(JNIEnv* env, jclass, jstring jpath) {
  const char* path = env->GetStringUTFChars(jpath, nullptr);
  Runtime::Current()->SetProcessDataDirectory(path);
  env->ReleaseStringUTFChars(jpath, path);
}

extern "C" JNIEXPORT jboolean JNICALL Java_Main_areClassesVerified(JNIEnv*,
                                                                   jclass,
                                                                   jobject loader) {
  ScopedObjectAccess soa(Thread::Current());
  StackHandleScope<2> hs(soa.Self());
  Handle<mirror::ClassLoader> h_loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(loader)));

  std::vector<const DexFile*> dex_files;
  VisitClassLoaderDexFiles(
      soa,
      h_loader,
      [&](const DexFile* dex_file) {
        dex_files.push_back(dex_file);
        return true;
      });

  MutableHandle<mirror::Class> h_class(hs.NewHandle<mirror::Class>(nullptr));
  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();

  bool is_first = true;
  bool all_verified = false;
  for (const DexFile* dex_file : dex_files) {
    for (uint16_t cdef_idx = 0; cdef_idx < dex_file->NumClassDefs(); ++cdef_idx) {
      const char* desc = dex_file->GetClassDescriptor(dex_file->GetClassDef(cdef_idx));
      h_class.Assign(class_linker->FindClass(soa.Self(), desc, h_loader));
      CHECK(h_class != nullptr) << "Could not find class " << desc;
      bool is_verified = h_class->IsVerified();
      if (is_first) {
        all_verified = is_verified;
        is_first = false;
      } else if (all_verified != is_verified) {
        // Classes should either all or none be verified.
        LOG(ERROR) << "areClassesVerified is inconsistent";
      }
    }
  }
  return all_verified ? JNI_TRUE : JNI_FALSE;
}

extern "C" JNIEXPORT bool JNICALL Java_Main_hasVdexFile(JNIEnv*,
                                                        jclass,
                                                        jobject loader) {
  ScopedObjectAccess soa(Thread::Current());
  StackHandleScope<1> hs(soa.Self());
  Handle<mirror::ClassLoader> h_loader = hs.NewHandle(soa.Decode<mirror::ClassLoader>(loader));

  std::vector<const DexFile*> dex_files;
  VisitClassLoaderDexFiles(
      soa,
      h_loader,
      [&](const DexFile* dex_file) {
        dex_files.push_back(dex_file);
        return true;
      });

  std::string dex_location = dex_files[0]->GetLocation();
  std::string odex_filename;
  std::string error_msg;
  if (!OatFileAssistant::DexLocationToOdexFilename(dex_location,
                                                   kRuntimeISA,
                                                   &odex_filename,
                                                   &error_msg)) {
    LOG(WARNING) << "Could not get odex filename for " << dex_location << ": " << error_msg;
    return false;
  }

  return OS::FileExists(GetVdexFilename(odex_filename).c_str());
}

extern "C" JNIEXPORT jboolean JNICALL Java_Main_isBackedByOatFile(JNIEnv*,
                                                                  jclass,
                                                                  jobject loader) {
  ScopedObjectAccess soa(Thread::Current());
  StackHandleScope<1> hs(soa.Self());
  Handle<mirror::ClassLoader> h_loader = hs.NewHandle(soa.Decode<mirror::ClassLoader>(loader));

  bool is_first = true;
  bool all_backed_by_oat = false;

  VisitClassLoaderDexFiles(
      soa,
      h_loader,
      [&](const DexFile* dex_file) {
        bool is_backed_by_oat = (dex_file->GetOatDexFile() != nullptr);
        if (is_first) {
          all_backed_by_oat = is_backed_by_oat;
          is_first = false;
        } else if (all_backed_by_oat != is_backed_by_oat) {
          // DexFiles should either all or none be backed by oat.
          LOG(ERROR) << "isBackedByOatFile is inconsistent";
        }
        return true;
      });
  return all_backed_by_oat ? JNI_TRUE : JNI_FALSE;
}

extern "C" JNIEXPORT jboolean JNICALL Java_Main_areClassesPreverified(JNIEnv*,
                                                                      jclass,
                                                                      jobject loader) {
  ScopedObjectAccess soa(Thread::Current());
  StackHandleScope<2> hs(soa.Self());
  Handle<mirror::ClassLoader> h_loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(loader)));

  std::vector<const DexFile*> dex_files;
  VisitClassLoaderDexFiles(
      soa,
      h_loader,
      [&](const DexFile* dex_file) {
        dex_files.push_back(dex_file);
        return true;
      });

  MutableHandle<mirror::Class> h_class(hs.NewHandle<mirror::Class>(nullptr));
  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();

  bool is_first = true;
  bool all_preverified = false;
  for (const DexFile* dex_file : dex_files) {
    for (uint16_t cdef_idx = 0; cdef_idx < dex_file->NumClassDefs(); ++cdef_idx) {
      const char* desc = dex_file->GetClassDescriptor(dex_file->GetClassDef(cdef_idx));
      h_class.Assign(class_linker->FindClass(soa.Self(), desc, h_loader));
      CHECK(h_class != nullptr) << "Could not find class " << desc;

      ClassStatus oat_file_class_status(ClassStatus::kNotReady);
      bool is_preverified = class_linker->VerifyClassUsingOatFile(
          soa.Self(), *dex_file, h_class, oat_file_class_status);

      if (is_first) {
        all_preverified = is_preverified;
        is_first = false;
      } else if (all_preverified != is_preverified) {
        // Classes should either all or none be preverified.
        LOG(ERROR) << "areClassesPreverified is inconsistent";
      }
    }
  }

  return all_preverified ? JNI_TRUE : JNI_FALSE;
}

extern "C" JNIEXPORT jint JNICALL Java_Main_getVdexCacheSize(JNIEnv*, jclass) {
  return static_cast<jint>(OatFileManager::kAnonymousVdexCacheSize);
}

extern "C" JNIEXPORT jboolean JNICALL Java_Main_isAnonymousVdexBasename(JNIEnv* env,
                                                                        jclass,
                                                                        jstring basename) {
  if (basename == nullptr) {
    return JNI_FALSE;
  }
  ScopedUtfChars basename_utf(env, basename);
  return OatFileAssistant::IsAnonymousVdexBasename(basename_utf.c_str()) ? JNI_TRUE : JNI_FALSE;
}

}  // namespace Test692VdexInmemLoader
}  // namespace art
