1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "JniInvocation.h"
18
19#include <dlfcn.h>
20#include <stdlib.h>
21#include <string.h>
22
23#include <cstddef>
24
25#define LOG_TAG "JniInvocation"
26#include "cutils/log.h"
27
28#ifdef HAVE_ANDROID_OS
29#include "cutils/properties.h"
30#endif
31
32JniInvocation* JniInvocation::jni_invocation_ = NULL;
33
34JniInvocation::JniInvocation() :
35    handle_(NULL),
36    JNI_GetDefaultJavaVMInitArgs_(NULL),
37    JNI_CreateJavaVM_(NULL),
38    JNI_GetCreatedJavaVMs_(NULL) {
39
40  LOG_ALWAYS_FATAL_IF(jni_invocation_ != NULL, "JniInvocation instance already initialized");
41  jni_invocation_ = this;
42}
43
44JniInvocation::~JniInvocation() {
45  jni_invocation_ = NULL;
46  if (handle_ != NULL) {
47    dlclose(handle_);
48  }
49}
50
51#ifdef HAVE_ANDROID_OS
52static const char* kLibrarySystemProperty = "persist.sys.dalvik.vm.lib";
53#endif
54static const char* kLibraryFallback = "libdvm.so";
55
56bool JniInvocation::Init(const char* library) {
57#ifdef HAVE_ANDROID_OS
58  char default_library[PROPERTY_VALUE_MAX];
59  property_get(kLibrarySystemProperty, default_library, kLibraryFallback);
60#else
61  const char* default_library = kLibraryFallback;
62#endif
63  if (library == NULL) {
64    library = default_library;
65  }
66
67  handle_ = dlopen(library, RTLD_NOW);
68  if (handle_ == NULL) {
69    if (strcmp(library, kLibraryFallback) == 0) {
70      // Nothing else to try.
71      ALOGE("Failed to dlopen %s: %s", library, dlerror());
72      return false;
73    }
74    // Note that this is enough to get something like the zygote
75    // running, we can't property_set here to fix this for the future
76    // because we are root and not the system user. See
77    // RuntimeInit.commonInit for where we fix up the property to
78    // avoid future fallbacks. http://b/11463182
79    ALOGW("Falling back from %s to %s after dlopen error: %s",
80          library, kLibraryFallback, dlerror());
81    library = kLibraryFallback;
82    handle_ = dlopen(library, RTLD_NOW);
83    if (handle_ == NULL) {
84      ALOGE("Failed to dlopen %s: %s", library, dlerror());
85      return false;
86    }
87  }
88  if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetDefaultJavaVMInitArgs_),
89                  "JNI_GetDefaultJavaVMInitArgs")) {
90    return false;
91  }
92  if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_),
93                  "JNI_CreateJavaVM")) {
94    return false;
95  }
96  if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetCreatedJavaVMs_),
97                  "JNI_GetCreatedJavaVMs")) {
98    return false;
99  }
100  return true;
101}
102
103jint JniInvocation::JNI_GetDefaultJavaVMInitArgs(void* vmargs) {
104  return JNI_GetDefaultJavaVMInitArgs_(vmargs);
105}
106
107jint JniInvocation::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
108  return JNI_CreateJavaVM_(p_vm, p_env, vm_args);
109}
110
111jint JniInvocation::JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count) {
112  return JNI_GetCreatedJavaVMs_(vms, size, vm_count);
113}
114
115bool JniInvocation::FindSymbol(void** pointer, const char* symbol) {
116  *pointer = dlsym(handle_, symbol);
117  if (*pointer == NULL) {
118    ALOGE("Failed to find symbol %s: %s\n", symbol, dlerror());
119    dlclose(handle_);
120    handle_ = NULL;
121    return false;
122  }
123  return true;
124}
125
126JniInvocation& JniInvocation::GetJniInvocation() {
127  LOG_ALWAYS_FATAL_IF(jni_invocation_ == NULL,
128                      "Failed to create JniInvocation instance before using JNI invocation API");
129  return *jni_invocation_;
130}
131
132extern "C" jint JNI_GetDefaultJavaVMInitArgs(void* vm_args) {
133  return JniInvocation::GetJniInvocation().JNI_GetDefaultJavaVMInitArgs(vm_args);
134}
135
136extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
137  return JniInvocation::GetJniInvocation().JNI_CreateJavaVM(p_vm, p_env, vm_args);
138}
139
140extern "C" jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count) {
141  return JniInvocation::GetJniInvocation().JNI_GetCreatedJavaVMs(vms, size, vm_count);
142}
143