dalvik_system_VMRuntime.cc revision f9c612f2e657adb0764285256b228622e0789254
1/*
2 * Copyright (C) 2008 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 <limits.h>
18
19#include "class_linker-inl.h"
20#include "common_throws.h"
21#include "debugger.h"
22#include "dex_file-inl.h"
23#include "gc/accounting/card_table-inl.h"
24#include "gc/allocator/dlmalloc.h"
25#include "gc/heap.h"
26#include "gc/space/dlmalloc_space.h"
27#include "intern_table.h"
28#include "jni_internal.h"
29#include "mirror/art_method-inl.h"
30#include "mirror/class-inl.h"
31#include "mirror/dex_cache-inl.h"
32#include "mirror/object-inl.h"
33#include "object_utils.h"
34#include "scoped_fast_native_object_access.h"
35#include "scoped_thread_state_change.h"
36#include "thread.h"
37#include "thread_list.h"
38#include "toStringArray.h"
39
40namespace art {
41
42static jfloat VMRuntime_getTargetHeapUtilization(JNIEnv*, jobject) {
43  return Runtime::Current()->GetHeap()->GetTargetHeapUtilization();
44}
45
46static void VMRuntime_nativeSetTargetHeapUtilization(JNIEnv*, jobject, jfloat target) {
47  Runtime::Current()->GetHeap()->SetTargetHeapUtilization(target);
48}
49
50static void VMRuntime_startJitCompilation(JNIEnv*, jobject) {
51}
52
53static void VMRuntime_disableJitCompilation(JNIEnv*, jobject) {
54}
55
56static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaElementClass,
57                                            jint length) {
58  ScopedFastNativeObjectAccess soa(env);
59  if (UNLIKELY(length < 0)) {
60    ThrowNegativeArraySizeException(length);
61    return nullptr;
62  }
63  mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass);
64  if (UNLIKELY(element_class == nullptr)) {
65    ThrowNullPointerException(NULL, "element class == null");
66    return nullptr;
67  }
68  if (UNLIKELY(element_class->IsPrimitiveVoid())) {
69    ThrowIllegalArgumentException(NULL, "Can't allocate an array of void");
70    return nullptr;
71  }
72  Runtime* runtime = Runtime::Current();
73  mirror::Class* array_class = runtime->GetClassLinker()->FindArrayClass(soa.Self(), element_class);
74  if (UNLIKELY(array_class == nullptr)) {
75    return nullptr;
76  }
77  gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentNonMovingAllocator();
78  mirror::Array* result = mirror::Array::Alloc<true>(soa.Self(), array_class, length,
79                                                     array_class->GetComponentSize(), allocator);
80  return soa.AddLocalReference<jobject>(result);
81}
82
83static jobject VMRuntime_newUnpaddedArray(JNIEnv* env, jobject, jclass javaElementClass,
84                                          jint length) {
85  ScopedFastNativeObjectAccess soa(env);
86  if (UNLIKELY(length < 0)) {
87    ThrowNegativeArraySizeException(length);
88    return nullptr;
89  }
90  mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass);
91  if (UNLIKELY(element_class == nullptr)) {
92    ThrowNullPointerException(NULL, "element class == null");
93    return nullptr;
94  }
95  if (UNLIKELY(element_class->IsPrimitiveVoid())) {
96    ThrowIllegalArgumentException(NULL, "Can't allocate an array of void");
97    return nullptr;
98  }
99  Runtime* runtime = Runtime::Current();
100  mirror::Class* array_class = runtime->GetClassLinker()->FindArrayClass(soa.Self(), element_class);
101  if (UNLIKELY(array_class == nullptr)) {
102    return nullptr;
103  }
104  gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentAllocator();
105  mirror::Array* result = mirror::Array::Alloc<true>(soa.Self(), array_class, length,
106                                                     array_class->GetComponentSize(), allocator,
107                                                     true);
108  return soa.AddLocalReference<jobject>(result);
109}
110
111static jlong VMRuntime_addressOf(JNIEnv* env, jobject, jobject javaArray) {
112  if (javaArray == NULL) {  // Most likely allocation failed
113    return 0;
114  }
115  ScopedFastNativeObjectAccess soa(env);
116  mirror::Array* array = soa.Decode<mirror::Array*>(javaArray);
117  if (!array->IsArrayInstance()) {
118    ThrowIllegalArgumentException(NULL, "not an array");
119    return 0;
120  }
121  if (Runtime::Current()->GetHeap()->IsMovableObject(array)) {
122    ThrowRuntimeException("Trying to get address of movable array object");
123    return 0;
124  }
125  return reinterpret_cast<uintptr_t>(array->GetRawData(array->GetClass()->GetComponentSize(), 0));
126}
127
128static void VMRuntime_clearGrowthLimit(JNIEnv*, jobject) {
129  Runtime::Current()->GetHeap()->ClearGrowthLimit();
130}
131
132static jboolean VMRuntime_isDebuggerActive(JNIEnv*, jobject) {
133  return Dbg::IsDebuggerActive();
134}
135
136static jobjectArray VMRuntime_properties(JNIEnv* env, jobject) {
137  return toStringArray(env, Runtime::Current()->GetProperties());
138}
139
140// This is for backward compatibility with dalvik which returned the
141// meaningless "." when no boot classpath or classpath was
142// specified. Unfortunately, some tests were using java.class.path to
143// lookup relative file locations, so they are counting on this to be
144// ".", presumably some applications or libraries could have as well.
145static const char* DefaultToDot(const std::string& class_path) {
146  return class_path.empty() ? "." : class_path.c_str();
147}
148
149static jstring VMRuntime_bootClassPath(JNIEnv* env, jobject) {
150  return env->NewStringUTF(DefaultToDot(Runtime::Current()->GetBootClassPathString()));
151}
152
153static jstring VMRuntime_classPath(JNIEnv* env, jobject) {
154  return env->NewStringUTF(DefaultToDot(Runtime::Current()->GetClassPathString()));
155}
156
157static jstring VMRuntime_vmVersion(JNIEnv* env, jobject) {
158  return env->NewStringUTF(Runtime::Current()->GetVersion());
159}
160
161static jstring VMRuntime_vmLibrary(JNIEnv* env, jobject) {
162  return env->NewStringUTF(kIsDebugBuild ? "libartd.so" : "libart.so");
163}
164
165static void VMRuntime_setTargetSdkVersionNative(JNIEnv* env, jobject, jint targetSdkVersion) {
166  // This is the target SDK version of the app we're about to run.
167  // Note that targetSdkVersion may be CUR_DEVELOPMENT (10000).
168  // Note that targetSdkVersion may be 0, meaning "current".
169  if (targetSdkVersion > 0 && targetSdkVersion <= 13 /* honeycomb-mr2 */) {
170    Runtime* runtime = Runtime::Current();
171    JavaVMExt* vm = runtime->GetJavaVM();
172    if (vm->check_jni) {
173      LOG(INFO) << "CheckJNI enabled: not enabling JNI app bug workarounds.";
174    } else {
175      LOG(INFO) << "Turning on JNI app bug workarounds for target SDK version "
176          << targetSdkVersion << "...";
177
178      vm->work_around_app_jni_bugs = true;
179    }
180  }
181}
182
183static void VMRuntime_registerNativeAllocation(JNIEnv* env, jobject, jint bytes) {
184  if (UNLIKELY(bytes < 0)) {
185    ScopedObjectAccess soa(env);
186    ThrowRuntimeException("allocation size negative %d", bytes);
187    return;
188  }
189  Runtime::Current()->GetHeap()->RegisterNativeAllocation(env, bytes);
190}
191
192static void VMRuntime_registerNativeFree(JNIEnv* env, jobject, jint bytes) {
193  if (UNLIKELY(bytes < 0)) {
194    ScopedObjectAccess soa(env);
195    ThrowRuntimeException("allocation size negative %d", bytes);
196    return;
197  }
198  Runtime::Current()->GetHeap()->RegisterNativeFree(env, bytes);
199}
200
201static void VMRuntime_updateProcessState(JNIEnv* env, jobject, jint process_state) {
202  Runtime::Current()->GetHeap()->UpdateProcessState(static_cast<gc::ProcessState>(process_state));
203}
204
205static void VMRuntime_trimHeap(JNIEnv*, jobject) {
206  Runtime::Current()->GetHeap()->Trim();
207}
208
209static void VMRuntime_concurrentGC(JNIEnv* env, jobject) {
210  Thread* self = ThreadForEnv(env);
211  Runtime::Current()->GetHeap()->ConcurrentGC(self);
212}
213
214typedef std::map<std::string, mirror::String*> StringTable;
215
216static void PreloadDexCachesStringsCallback(mirror::Object** root, void* arg,
217                                            uint32_t /*thread_id*/, RootType /*root_type*/)
218    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
219  StringTable& table = *reinterpret_cast<StringTable*>(arg);
220  mirror::String* string = const_cast<mirror::Object*>(*root)->AsString();
221  table[string->ToModifiedUtf8()] = string;
222}
223
224// Based on ClassLinker::ResolveString.
225static void PreloadDexCachesResolveString(SirtRef<mirror::DexCache>& dex_cache,
226                                          uint32_t string_idx,
227                                          StringTable& strings)
228    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
229  mirror::String* string = dex_cache->GetResolvedString(string_idx);
230  if (string != NULL) {
231    return;
232  }
233  const DexFile* dex_file = dex_cache->GetDexFile();
234  const char* utf8 = dex_file->StringDataByIdx(string_idx);
235  string = strings[utf8];
236  if (string == NULL) {
237    return;
238  }
239  // LOG(INFO) << "VMRuntime.preloadDexCaches resolved string=" << utf8;
240  dex_cache->SetResolvedString(string_idx, string);
241}
242
243// Based on ClassLinker::ResolveType.
244static void PreloadDexCachesResolveType(mirror::DexCache* dex_cache, uint32_t type_idx)
245    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
246  mirror::Class* klass = dex_cache->GetResolvedType(type_idx);
247  if (klass != NULL) {
248    return;
249  }
250  const DexFile* dex_file = dex_cache->GetDexFile();
251  const char* class_name = dex_file->StringByTypeIdx(type_idx);
252  ClassLinker* linker = Runtime::Current()->GetClassLinker();
253  if (class_name[1] == '\0') {
254    klass = linker->FindPrimitiveClass(class_name[0]);
255  } else {
256    klass = linker->LookupClass(class_name, NULL);
257  }
258  if (klass == NULL) {
259    return;
260  }
261  // LOG(INFO) << "VMRuntime.preloadDexCaches resolved klass=" << class_name;
262  dex_cache->SetResolvedType(type_idx, klass);
263  // Skip uninitialized classes because filled static storage entry implies it is initialized.
264  if (!klass->IsInitialized()) {
265    // LOG(INFO) << "VMRuntime.preloadDexCaches uninitialized klass=" << class_name;
266    return;
267  }
268  // LOG(INFO) << "VMRuntime.preloadDexCaches static storage klass=" << class_name;
269}
270
271// Based on ClassLinker::ResolveField.
272static void PreloadDexCachesResolveField(SirtRef<mirror::DexCache>& dex_cache,
273                                         uint32_t field_idx,
274                                         bool is_static)
275    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
276  mirror::ArtField* field = dex_cache->GetResolvedField(field_idx);
277  if (field != NULL) {
278    return;
279  }
280  const DexFile* dex_file = dex_cache->GetDexFile();
281  const DexFile::FieldId& field_id = dex_file->GetFieldId(field_idx);
282  mirror::Class* klass = dex_cache->GetResolvedType(field_id.class_idx_);
283  if (klass == NULL) {
284    return;
285  }
286  if (is_static) {
287    field = klass->FindStaticField(dex_cache.get(), field_idx);
288  } else {
289    field = klass->FindInstanceField(dex_cache.get(), field_idx);
290  }
291  if (field == NULL) {
292    return;
293  }
294  // LOG(INFO) << "VMRuntime.preloadDexCaches resolved field " << PrettyField(field);
295  dex_cache->SetResolvedField(field_idx, field);
296}
297
298// Based on ClassLinker::ResolveMethod.
299static void PreloadDexCachesResolveMethod(SirtRef<mirror::DexCache>& dex_cache,
300                                          uint32_t method_idx,
301                                          InvokeType invoke_type)
302    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
303  mirror::ArtMethod* method = dex_cache->GetResolvedMethod(method_idx);
304  if (method != NULL) {
305    return;
306  }
307  const DexFile* dex_file = dex_cache->GetDexFile();
308  const DexFile::MethodId& method_id = dex_file->GetMethodId(method_idx);
309  mirror::Class* klass = dex_cache->GetResolvedType(method_id.class_idx_);
310  if (klass == NULL) {
311    return;
312  }
313  switch (invoke_type) {
314    case kDirect:
315    case kStatic:
316      method = klass->FindDirectMethod(dex_cache.get(), method_idx);
317      break;
318    case kInterface:
319      method = klass->FindInterfaceMethod(dex_cache.get(), method_idx);
320      break;
321    case kSuper:
322    case kVirtual:
323      method = klass->FindVirtualMethod(dex_cache.get(), method_idx);
324      break;
325    default:
326      LOG(FATAL) << "Unreachable - invocation type: " << invoke_type;
327  }
328  if (method == NULL) {
329    return;
330  }
331  // LOG(INFO) << "VMRuntime.preloadDexCaches resolved method " << PrettyMethod(method);
332  dex_cache->SetResolvedMethod(method_idx, method);
333}
334
335struct DexCacheStats {
336    uint32_t num_strings;
337    uint32_t num_types;
338    uint32_t num_fields;
339    uint32_t num_methods;
340    DexCacheStats() : num_strings(0),
341                      num_types(0),
342                      num_fields(0),
343                      num_methods(0) {}
344};
345
346static const bool kPreloadDexCachesEnabled = true;
347
348// Disabled because it takes a long time (extra half second) but
349// gives almost no benefit in terms of saving private dirty pages.
350static const bool kPreloadDexCachesStrings = false;
351
352static const bool kPreloadDexCachesTypes = true;
353static const bool kPreloadDexCachesFieldsAndMethods = true;
354
355static const bool kPreloadDexCachesCollectStats = true;
356
357static void PreloadDexCachesStatsTotal(DexCacheStats* total) {
358  if (!kPreloadDexCachesCollectStats) {
359    return;
360  }
361
362  ClassLinker* linker = Runtime::Current()->GetClassLinker();
363  const std::vector<const DexFile*>& boot_class_path = linker->GetBootClassPath();
364  for (size_t i = 0; i< boot_class_path.size(); i++) {
365    const DexFile* dex_file = boot_class_path[i];
366    CHECK(dex_file != NULL);
367    total->num_strings += dex_file->NumStringIds();
368    total->num_fields += dex_file->NumFieldIds();
369    total->num_methods += dex_file->NumMethodIds();
370    total->num_types += dex_file->NumTypeIds();
371  }
372}
373
374static void PreloadDexCachesStatsFilled(DexCacheStats* filled)
375    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
376  if (!kPreloadDexCachesCollectStats) {
377      return;
378  }
379  ClassLinker* linker = Runtime::Current()->GetClassLinker();
380  const std::vector<const DexFile*>& boot_class_path = linker->GetBootClassPath();
381  for (size_t i = 0; i< boot_class_path.size(); i++) {
382    const DexFile* dex_file = boot_class_path[i];
383    CHECK(dex_file != NULL);
384    mirror::DexCache* dex_cache = linker->FindDexCache(*dex_file);
385    for (size_t i = 0; i < dex_cache->NumStrings(); i++) {
386      mirror::String* string = dex_cache->GetResolvedString(i);
387      if (string != NULL) {
388        filled->num_strings++;
389      }
390    }
391    for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
392      mirror::Class* klass = dex_cache->GetResolvedType(i);
393      if (klass != NULL) {
394        filled->num_types++;
395      }
396    }
397    for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) {
398      mirror::ArtField* field = dex_cache->GetResolvedField(i);
399      if (field != NULL) {
400        filled->num_fields++;
401      }
402    }
403    for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) {
404      mirror::ArtMethod* method = dex_cache->GetResolvedMethod(i);
405      if (method != NULL) {
406        filled->num_methods++;
407      }
408    }
409  }
410}
411
412// TODO: http://b/11309598 This code was ported over based on the
413// Dalvik version. However, ART has similar code in other places such
414// as the CompilerDriver. This code could probably be refactored to
415// serve both uses.
416static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) {
417  if (!kPreloadDexCachesEnabled) {
418    return;
419  }
420
421  ScopedObjectAccess soa(env);
422
423  DexCacheStats total;
424  DexCacheStats before;
425  if (kPreloadDexCachesCollectStats) {
426    LOG(INFO) << "VMRuntime.preloadDexCaches starting";
427    PreloadDexCachesStatsTotal(&total);
428    PreloadDexCachesStatsFilled(&before);
429  }
430
431  Runtime* runtime = Runtime::Current();
432  ClassLinker* linker = runtime->GetClassLinker();
433  Thread* self = ThreadForEnv(env);
434
435  // We use a std::map to avoid heap allocating StringObjects to lookup in gDvm.literalStrings
436  StringTable strings;
437  if (kPreloadDexCachesStrings) {
438    runtime->GetInternTable()->VisitRoots(PreloadDexCachesStringsCallback, &strings, false, false);
439  }
440
441  const std::vector<const DexFile*>& boot_class_path = linker->GetBootClassPath();
442  for (size_t i = 0; i< boot_class_path.size(); i++) {
443    const DexFile* dex_file = boot_class_path[i];
444    CHECK(dex_file != NULL);
445    SirtRef<mirror::DexCache> dex_cache(self, linker->FindDexCache(*dex_file));
446
447    if (kPreloadDexCachesStrings) {
448      for (size_t i = 0; i < dex_cache->NumStrings(); i++) {
449        PreloadDexCachesResolveString(dex_cache, i, strings);
450      }
451    }
452
453    if (kPreloadDexCachesTypes) {
454      for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
455        PreloadDexCachesResolveType(dex_cache.get(), i);
456      }
457    }
458
459    if (kPreloadDexCachesFieldsAndMethods) {
460      for (size_t class_def_index = 0;
461           class_def_index < dex_file->NumClassDefs();
462           class_def_index++) {
463        const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
464        const byte* class_data = dex_file->GetClassData(class_def);
465        if (class_data == NULL) {
466          continue;
467        }
468        ClassDataItemIterator it(*dex_file, class_data);
469        for (; it.HasNextStaticField(); it.Next()) {
470          uint32_t field_idx = it.GetMemberIndex();
471          PreloadDexCachesResolveField(dex_cache, field_idx, true);
472        }
473        for (; it.HasNextInstanceField(); it.Next()) {
474          uint32_t field_idx = it.GetMemberIndex();
475          PreloadDexCachesResolveField(dex_cache, field_idx, false);
476        }
477        for (; it.HasNextDirectMethod(); it.Next()) {
478          uint32_t method_idx = it.GetMemberIndex();
479          InvokeType invoke_type = it.GetMethodInvokeType(class_def);
480          PreloadDexCachesResolveMethod(dex_cache, method_idx, invoke_type);
481        }
482        for (; it.HasNextVirtualMethod(); it.Next()) {
483          uint32_t method_idx = it.GetMemberIndex();
484          InvokeType invoke_type = it.GetMethodInvokeType(class_def);
485          PreloadDexCachesResolveMethod(dex_cache, method_idx, invoke_type);
486        }
487      }
488    }
489  }
490
491  if (kPreloadDexCachesCollectStats) {
492    DexCacheStats after;
493    PreloadDexCachesStatsFilled(&after);
494    LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches strings total=%d before=%d after=%d",
495                              total.num_strings, before.num_strings, after.num_strings);
496    LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches types total=%d before=%d after=%d",
497                              total.num_types, before.num_types, after.num_types);
498    LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches fields total=%d before=%d after=%d",
499                              total.num_fields, before.num_fields, after.num_fields);
500    LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches methods total=%d before=%d after=%d",
501                              total.num_methods, before.num_methods, after.num_methods);
502    LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches finished");
503  }
504}
505
506
507/*
508 * This is called by the framework when it knows the application directory and
509 * process name.  We use this information to start up the sampling profiler for
510 * for ART.
511 */
512static void VMRuntime_registerAppInfo(JNIEnv* env, jclass, jstring appDir, jstring procName) {
513  const char *appDirChars = env->GetStringUTFChars(appDir, NULL);
514  const char *procNameChars = env->GetStringUTFChars(procName, NULL);
515  std::string profileFile = std::string(appDirChars) + "/art-profile-" + std::string(procNameChars);
516  Runtime::Current()->StartProfiler(profileFile.c_str());
517  env->ReleaseStringUTFChars(appDir, appDirChars);
518  env->ReleaseStringUTFChars(procName, procNameChars);
519}
520
521static JNINativeMethod gMethods[] = {
522  NATIVE_METHOD(VMRuntime, addressOf, "!(Ljava/lang/Object;)J"),
523  NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"),
524  NATIVE_METHOD(VMRuntime, classPath, "()Ljava/lang/String;"),
525  NATIVE_METHOD(VMRuntime, clearGrowthLimit, "()V"),
526  NATIVE_METHOD(VMRuntime, concurrentGC, "()V"),
527  NATIVE_METHOD(VMRuntime, disableJitCompilation, "()V"),
528  NATIVE_METHOD(VMRuntime, getTargetHeapUtilization, "()F"),
529  NATIVE_METHOD(VMRuntime, isDebuggerActive, "()Z"),
530  NATIVE_METHOD(VMRuntime, nativeSetTargetHeapUtilization, "(F)V"),
531  NATIVE_METHOD(VMRuntime, newNonMovableArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"),
532  NATIVE_METHOD(VMRuntime, newUnpaddedArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"),
533  NATIVE_METHOD(VMRuntime, properties, "()[Ljava/lang/String;"),
534  NATIVE_METHOD(VMRuntime, setTargetSdkVersionNative, "(I)V"),
535  NATIVE_METHOD(VMRuntime, registerNativeAllocation, "(I)V"),
536  NATIVE_METHOD(VMRuntime, registerNativeFree, "(I)V"),
537  NATIVE_METHOD(VMRuntime, updateProcessState, "(I)V"),
538  NATIVE_METHOD(VMRuntime, startJitCompilation, "()V"),
539  NATIVE_METHOD(VMRuntime, trimHeap, "()V"),
540  NATIVE_METHOD(VMRuntime, vmVersion, "()Ljava/lang/String;"),
541  NATIVE_METHOD(VMRuntime, vmLibrary, "()Ljava/lang/String;"),
542  NATIVE_METHOD(VMRuntime, preloadDexCaches, "()V"),
543  NATIVE_METHOD(VMRuntime, registerAppInfo, "(Ljava/lang/String;Ljava/lang/String;)V"),
544};
545
546void register_dalvik_system_VMRuntime(JNIEnv* env) {
547  REGISTER_NATIVE_METHODS("dalvik/system/VMRuntime");
548}
549
550}  // namespace art
551