blob: a5e0277a475942943780f553eb8d3d82efcb0b4b [file] [log] [blame]
/*
* Copyright 2019 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.
*/
#pragma once
#include <jni.h>
#include <vector>
#include <string>
#define CHECK_FOR_JNI_EXCEPTION_AND_RETURN(A) if (RawExceptionCheck()) { \
std::string exception_msg = GetExceptionMessage(); \
ALOGW("%s", exception_msg.c_str()); return A; }
namespace tuningfork {
namespace jni {
// Management of jni envs and the app context.
void Init(JNIEnv* env, jobject ctx);
bool IsValid();
JNIEnv* Env();
void DetachThread();
jobject AppContextGlobalRef();
// It is the responsibility of the caller to delete the returned local reference.
jclass FindClass(const char* class_name);
// A wrapper around a jni jstring.
// Releases the jstring and any c string pointer generated from it upon destruction.
class String {
jstring j_str_;
const char* c_str_;
public:
String(const char* s) : j_str_(Env()->NewStringUTF(s)), c_str_(nullptr) {}
String(jstring s) : j_str_(s), c_str_(nullptr) {}
String(String&& rhs) : j_str_(rhs.j_str_), c_str_(rhs.c_str_) {
rhs.j_str_ = nullptr;
rhs.c_str_ = nullptr;
}
String(const String& rhs) : j_str_(rhs.j_str_), c_str_(nullptr) {
if (j_str_!=nullptr) {
j_str_ = reinterpret_cast<jstring>(Env()->NewLocalRef(j_str_));
}
}
String& operator=(const String& rhs) {
if (this!=&rhs) {
Release();
if (rhs.j_str_!=nullptr) {
j_str_ = reinterpret_cast<jstring>(Env()->NewLocalRef(rhs.j_str_));
}
}
return *this;
}
jstring J() const { return j_str_;}
const char* C() {
if (c_str_==nullptr && j_str_!=nullptr) {
c_str_ = Env()->GetStringUTFChars(j_str_, nullptr);
}
return c_str_;
}
~String() {
Release();
}
void Release() {
if (c_str_!=nullptr) {
Env()->ReleaseStringUTFChars(j_str_, c_str_);
c_str_ = nullptr;
}
if (j_str_!=nullptr) {
Env()->DeleteLocalRef(j_str_);
j_str_ = nullptr;
}
}
};
// This class takes ownership of the jni object and jni class and calls DeleteLocalRef
// on destruction.
// The copy constructor and l-value operator= are deleted to avoid creating and deleting
// local references unnecessarily. Use the r-value move versions instead.
// NB you cannot share these objects between threads. Create global refs in order to do that.
class LocalObject {
jobject obj_;
jclass clz_;
public:
static constexpr int BAD_FIELD = -1;
LocalObject(jobject o = nullptr, jclass c = nullptr) : obj_(o), clz_(c) {}
~LocalObject() { Release(); }
LocalObject(LocalObject&& o) : obj_(o.obj_), clz_(o.clz_) {
o.obj_ = nullptr;
o.clz_ = nullptr;
}
LocalObject(const LocalObject& o) = delete;
LocalObject& operator=(const LocalObject& o) = delete;
LocalObject& operator=(LocalObject&& o) {
if (obj_!=o.obj_) {
if (obj_ != nullptr) {
Env()->DeleteLocalRef(obj_);
}
obj_ = o.obj_;
}
if (clz_!=o.clz_) {
if (clz_ != nullptr) {
Env()->DeleteLocalRef(clz_);
}
clz_ = o.clz_;
}
o.obj_ = nullptr;
o.clz_ = nullptr;
return *this;
}
jobject ObjNewRef() const {
if(obj_ != nullptr)
return Env()->NewLocalRef(obj_);
return obj_;
}
jclass ClassNewRef() const {
if(clz_ != nullptr)
return (jclass)Env()->NewLocalRef(clz_);
return clz_;
}
jobjectArray AsObjectArray() const {
return reinterpret_cast<jobjectArray>(obj_);
}
bool IsNull() const { return obj_==nullptr; }
bool ClassIsNull() const { return clz_==nullptr; }
operator jobject() const { return obj_; }
operator jclass() const { return clz_; }
void SetObj(jobject o) {
if (obj_!= nullptr)
Env()->DeleteLocalRef(obj_);
obj_ = o;
}
void SetClass(jclass c) {
if (clz_!= nullptr)
Env()->DeleteLocalRef(clz_);
clz_ = c;
}
// Set clz_ to the class with the given name or get the class from the
// object if clz_to is missing / empty.
bool Cast(const std::string& clz_to="") {
jclass c;
if (clz_to.empty()) {
if (obj_ == nullptr)
c = nullptr;
else
c = Env()->GetObjectClass(obj_);
} else {
c = FindClass(clz_to.c_str());
}
if (c==nullptr) return false;
SetClass(c);
return true;
}
// These methods take a variable number of arguments and have the return the type indicated.
// The arguments are passed directly to JNI and it's not type-safe, so:
// All object arguments should be jobjects, NOT LocalObject.
// All string arguments should be jstrings, NOT String.
jobject CallObjectMethod(const char* name, const char* sig, ...) const;
jobject CallStaticObjectMethod(const char* name, const char* sig, ...) const;
jni::String CallStringMethod(const char* name, const char* sig, ...) const;
void CallVoidMethod(const char* name, const char* sig, ...) const;
int CallIntMethod(const char* name, const char* sig, ...) const;
// Returns a null object if the field could not be found (and exception will be set)
LocalObject GetObjectField(const char* field_name, const char* sig) const;
// Returns BAD_FIELD is the field could not be found (and exception is set)
int GetIntField(const char* field_name) const;
private:
void Release() {
if (clz_ != nullptr) {
Env()->DeleteLocalRef(clz_);
}
if (obj_ != nullptr) {
Env()->DeleteLocalRef(obj_);
}
obj_ = nullptr;
clz_ = nullptr;
}
};
LocalObject NewObjectV(const char * cclz, const char* ctorSig, va_list argptr);
LocalObject NewObject(const char * cclz, const char* ctorSig, ...);
inline bool RawExceptionCheck() { return Env()->ExceptionCheck(); }
// This will clear the exception and get the exception message.
std::string GetExceptionMessage();
// Do a RawExceptionCheck and return the result of it, filling in the msg with the
// exception message if one was thrown. Also clears the exception.
bool CheckForException(std::string& msg);
std::vector<char> GetByteArrayBytesAndDeleteRef(jbyteArray jbs);
// Debugging
#ifndef NDEBUG
void DumpLocalRefTable();
#else
inline void DumpLocalRefTable() {}
#endif
} // namespace jni
} // namespace tuningfork