1/* Copyright (C) 2017 The Android Open Source Project
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3 *
4 * This file implements interfaces from the file jvmti.h. This implementation
5 * is licensed under the same terms as the file jvmti.h.  The
6 * copyright and license information for the file jvmti.h follows.
7 *
8 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
9 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
10 *
11 * This code is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License version 2 only, as
13 * published by the Free Software Foundation.  Oracle designates this
14 * particular file as subject to the "Classpath" exception as provided
15 * by Oracle in the LICENSE file that accompanied this code.
16 *
17 * This code is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 * version 2 for more details (a copy is included in the LICENSE file that
21 * accompanied this code).
22 *
23 * You should have received a copy of the GNU General Public License version
24 * 2 along with this work; if not, write to the Free Software Foundation,
25 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
26 *
27 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
28 * or visit www.oracle.com if you need additional information or have any
29 * questions.
30 */
31
32#include "ti_properties.h"
33
34#include <string.h>
35#include <vector>
36
37#include "jni.h"
38#include "ScopedLocalRef.h"
39#include "ScopedUtfChars.h"
40
41#include "art_jvmti.h"
42#include "runtime.h"
43#include "thread-inl.h"
44#include "ti_phase.h"
45#include "well_known_classes.h"
46
47namespace openjdkjvmti {
48
49// Hardcoded properties. Tests ensure that these are consistent with libcore's view, as seen
50// in System.java and AndroidHardcodedSystemProperties.java.
51static constexpr const char* kProperties[][2] = {
52    // Recommended by the spec.
53    { "java.vm.vendor", "The Android Project" },
54    { "java.vm.version", "2.1.0" },  // This is Runtime::GetVersion().
55    { "java.vm.name", "Dalvik" },
56    // Android does not provide java.vm.info.
57    //
58    // These are other values provided by AndroidHardcodedSystemProperties.
59    { "java.class.version", "50.0" },
60    { "java.version", "0" },
61    { "java.compiler", "" },
62    { "java.ext.dirs", "" },
63
64    { "java.specification.name", "Dalvik Core Library" },
65    { "java.specification.vendor", "The Android Project" },
66    { "java.specification.version", "0.9" },
67
68    { "java.vendor", "The Android Project" },
69    { "java.vendor.url", "http://www.android.com/" },
70    { "java.vm.name", "Dalvik" },
71    { "java.vm.specification.name", "Dalvik Virtual Machine Specification" },
72    { "java.vm.specification.vendor", "The Android Project" },
73    { "java.vm.specification.version", "0.9" },
74    { "java.vm.vendor", "The Android Project" },
75
76    { "java.vm.vendor.url", "http://www.android.com/" },
77
78    { "java.net.preferIPv6Addresses", "false" },
79
80    { "file.encoding", "UTF-8" },
81
82    { "file.separator", "/" },
83    { "line.separator", "\n" },
84    { "path.separator", ":" },
85
86    { "os.name", "Linux" },
87};
88static constexpr size_t kPropertiesSize = arraysize(kProperties);
89static constexpr const char* kPropertyLibraryPath = "java.library.path";
90static constexpr const char* kPropertyClassPath = "java.class.path";
91
92jvmtiError PropertiesUtil::GetSystemProperties(jvmtiEnv* env,
93                                               jint* count_ptr,
94                                               char*** property_ptr) {
95  if (count_ptr == nullptr || property_ptr == nullptr) {
96    return ERR(NULL_POINTER);
97  }
98  jvmtiError array_alloc_result;
99  JvmtiUniquePtr<char*[]> array_data_ptr = AllocJvmtiUniquePtr<char*[]>(env,
100                                                                        kPropertiesSize + 2,
101                                                                        &array_alloc_result);
102  if (array_data_ptr == nullptr) {
103    return array_alloc_result;
104  }
105
106  std::vector<JvmtiUniquePtr<char[]>> property_copies;
107
108  {
109    jvmtiError libpath_result;
110    JvmtiUniquePtr<char[]> libpath_data = CopyString(env, kPropertyLibraryPath, &libpath_result);
111    if (libpath_data == nullptr) {
112      return libpath_result;
113    }
114    array_data_ptr.get()[0] = libpath_data.get();
115    property_copies.push_back(std::move(libpath_data));
116  }
117
118  {
119    jvmtiError classpath_result;
120    JvmtiUniquePtr<char[]> classpath_data = CopyString(env, kPropertyClassPath, &classpath_result);
121    if (classpath_data == nullptr) {
122      return classpath_result;
123    }
124    array_data_ptr.get()[1] = classpath_data.get();
125    property_copies.push_back(std::move(classpath_data));
126  }
127
128  for (size_t i = 0; i != kPropertiesSize; ++i) {
129    jvmtiError data_result;
130    JvmtiUniquePtr<char[]> data = CopyString(env, kProperties[i][0], &data_result);
131    if (data == nullptr) {
132      return data_result;
133    }
134    array_data_ptr.get()[i + 2] = data.get();
135    property_copies.push_back(std::move(data));
136  }
137
138  // Everything is OK, release the data.
139  *count_ptr = kPropertiesSize + 2;
140  *property_ptr = array_data_ptr.release();
141  for (auto& uptr : property_copies) {
142    uptr.release();
143  }
144
145  return ERR(NONE);
146}
147
148static jvmtiError Copy(jvmtiEnv* env, const char* in, char** out) {
149  jvmtiError result;
150  JvmtiUniquePtr<char[]> data = CopyString(env, in, &result);
151  *out = data.release();
152  return result;
153}
154
155// See dalvik_system_VMRuntime.cpp.
156static const char* DefaultToDot(const std::string& class_path) {
157  return class_path.empty() ? "." : class_path.c_str();
158}
159
160// Handle kPropertyLibraryPath.
161static jvmtiError GetLibraryPath(jvmtiEnv* env, char** value_ptr) {
162  const std::vector<std::string>& runtime_props = art::Runtime::Current()->GetProperties();
163  for (const std::string& prop_assignment : runtime_props) {
164    size_t assign_pos = prop_assignment.find('=');
165    if (assign_pos != std::string::npos && assign_pos > 0) {
166      if (prop_assignment.substr(0, assign_pos) == kPropertyLibraryPath) {
167        return Copy(env, prop_assignment.substr(assign_pos + 1).c_str(), value_ptr);
168      }
169    }
170  }
171  if (!PhaseUtil::IsLivePhase()) {
172    return ERR(NOT_AVAILABLE);
173  }
174  // We expect this call to be rare. So don't optimize.
175  DCHECK(art::Thread::Current() != nullptr);
176  JNIEnv* jni_env = art::Thread::Current()->GetJniEnv();
177  jmethodID get_prop = jni_env->GetStaticMethodID(art::WellKnownClasses::java_lang_System,
178                                                  "getProperty",
179                                                  "(Ljava/lang/String;)Ljava/lang/String;");
180  CHECK(get_prop != nullptr);
181
182  ScopedLocalRef<jobject> input_str(jni_env, jni_env->NewStringUTF(kPropertyLibraryPath));
183  if (input_str.get() == nullptr) {
184    jni_env->ExceptionClear();
185    return ERR(OUT_OF_MEMORY);
186  }
187
188  ScopedLocalRef<jobject> prop_res(
189      jni_env, jni_env->CallStaticObjectMethod(art::WellKnownClasses::java_lang_System,
190                                               get_prop,
191                                               input_str.get()));
192  if (jni_env->ExceptionCheck() == JNI_TRUE) {
193    jni_env->ExceptionClear();
194    return ERR(INTERNAL);
195  }
196  if (prop_res.get() == nullptr) {
197    *value_ptr = nullptr;
198    return ERR(NONE);
199  }
200
201  ScopedUtfChars chars(jni_env, reinterpret_cast<jstring>(prop_res.get()));
202  return Copy(env, chars.c_str(), value_ptr);
203}
204
205jvmtiError PropertiesUtil::GetSystemProperty(jvmtiEnv* env,
206                                             const char* property,
207                                             char** value_ptr) {
208  if (property == nullptr || value_ptr == nullptr) {
209    return ERR(NULL_POINTER);
210  }
211
212  if (strcmp(property, kPropertyLibraryPath) == 0) {
213    return GetLibraryPath(env, value_ptr);
214  }
215
216  if (strcmp(property, kPropertyClassPath) == 0) {
217    return Copy(env, DefaultToDot(art::Runtime::Current()->GetClassPathString()), value_ptr);
218  }
219
220  for (size_t i = 0; i != kPropertiesSize; ++i) {
221    if (strcmp(property, kProperties[i][0]) == 0) {
222      return Copy(env, kProperties[i][1], value_ptr);
223    }
224  }
225
226  return ERR(NOT_AVAILABLE);
227}
228
229jvmtiError PropertiesUtil::SetSystemProperty(jvmtiEnv* env ATTRIBUTE_UNUSED,
230                                             const char* property ATTRIBUTE_UNUSED,
231                                             const char* value ATTRIBUTE_UNUSED) {
232  // We do not allow manipulation of any property here.
233  return ERR(NOT_AVAILABLE);
234}
235
236}  // namespace openjdkjvmti
237