blob: 98a65fdc49f5b971732e0410cc28fa0a72398abd [file] [log] [blame]
// Copyright 2017 The Cobalt Authors. All Rights Reserved.
//
// 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 "starboard/android/shared/jni_env_ext.h"
#include <android/native_activity.h>
#include <jni.h>
#include <algorithm>
#include <string>
#include "starboard/thread.h"
namespace {
SbThreadLocalKey g_tls_key = kSbThreadLocalKeyInvalid;
JavaVM* g_vm = NULL;
jobject g_application_class_loader = NULL;
jobject g_starboard_bridge = NULL;
void Destroy(void* value) {
// OnThreadShutdown() must be called on each thread before it is destroyed.
SB_DCHECK(value == NULL);
}
} // namespace
namespace starboard {
namespace android {
namespace shared {
// Warning: use __android_log_write for logging in this file.
// static
void JniEnvExt::Initialize(JniEnvExt* env, jobject starboard_bridge) {
SB_DCHECK(g_tls_key == kSbThreadLocalKeyInvalid);
g_tls_key = SbThreadCreateLocalKey(Destroy);
SB_DCHECK(g_vm == NULL);
env->GetJavaVM(&g_vm);
SB_DCHECK(g_application_class_loader == NULL);
g_application_class_loader =
env->ConvertLocalRefToGlobalRef(env->CallObjectMethodOrAbort(
env->GetObjectClass(starboard_bridge), "getClassLoader",
"()Ljava/lang/ClassLoader;"));
SB_DCHECK(g_starboard_bridge == NULL);
g_starboard_bridge = env->NewGlobalRef(starboard_bridge);
}
// static
void JniEnvExt::OnThreadShutdown() {
// We must call DetachCurrentThread() before exiting, if we have ever
// previously called AttachCurrentThread() on it.
// http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html
if (SbThreadGetLocalValue(g_tls_key)) {
g_vm->DetachCurrentThread();
SbThreadSetLocalValue(g_tls_key, NULL);
}
}
JniEnvExt* JniEnvExt::Get() {
JNIEnv* env = nullptr;
if (JNI_OK != g_vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6)) {
// Tell the JVM our thread name so it doesn't change it.
char thread_name[16];
SbThreadGetName(thread_name, sizeof(thread_name));
JavaVMAttachArgs args{JNI_VERSION_1_6, thread_name, NULL};
g_vm->AttachCurrentThread(&env, &args);
// We don't use the value, but any non-NULL means we have to detach.
SbThreadSetLocalValue(g_tls_key, env);
}
// The downcast is safe since we only add methods, not fields.
return static_cast<JniEnvExt*>(env);
}
jobject JniEnvExt::GetStarboardBridge() {
return g_starboard_bridge;
}
jclass JniEnvExt::FindClassExtOrAbort(const char* name) {
// Convert the JNI FindClass name with slashes to the "binary name" with dots
// for ClassLoader.loadClass().
::std::string dot_name = name;
::std::replace(dot_name.begin(), dot_name.end(), '/', '.');
jstring jname = NewStringUTF(dot_name.c_str());
AbortOnException();
jobject clazz_obj =
CallObjectMethodOrAbort(g_application_class_loader, "loadClass",
"(Ljava/lang/String;)Ljava/lang/Class;", jname);
DeleteLocalRef(jname);
return static_cast<jclass>(clazz_obj);
}
} // namespace shared
} // namespace android
} // namespace starboard