Commit 0aa08b45 authored by johan's avatar johan

Add Java interface

+add license files to clarify the status of the files copyright mapbox introduced.

Work in Progress, TODO:
- better testing
- add peer device status management functions
- complete doc
parent de7e3446
......@@ -26,7 +26,7 @@ include(CMakePushCheckState)
include(CMakePackageConfigHelpers)
cmake_minimum_required(VERSION 3.0)
project(lime VERSION 0.0.1 LANGUAGES CXX C)
project(lime VERSION 0.0.1 LANGUAGES CXX C Java)
set(LIME_SO_VERSION "0")
set(LIME_VERSION ${PROJECT_VERSION})
......@@ -39,6 +39,7 @@ option(ENABLE_CURVE448 "Enable support of Curve 448(goldilock)." YES)
option(ENABLE_UNIT_TESTS "Enable compilation of unit tests." YES)
option(ENABLE_PROFILING "Enable profiling, GCC only" NO)
option(ENABLE_C_INTERFACE "Enable support of C89 foreign function interface" NO)
option(ENABLE_JNI "Enable support of Java foreign function interface" NO)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
......@@ -93,7 +94,7 @@ else()
if (ENABLE_PROFILING)
list(APPEND STRICT_OPTIONS_CXX "-g -pg")
endif()
list(APPEND STRICT_OPTIONS_CXX "-std=c++11")
list(APPEND STRICT_OPTIONS_CXX "-std=c++14")
#list(APPEND STRICT_OPTIONS_CPP "-Wall" "-Wuninitialized" "-Wno-error=deprecated-declarations") # turn off deprecated-declaration warning to avoid being flooded by soci.h
list(APPEND STRICT_OPTIONS_CPP "-Wall" "-Wuninitialized" "-Wno-deprecated-declarations" "-Wno-missing-field-initializers")
if(ENABLE_C_INTERFACE)
......@@ -134,6 +135,15 @@ if(ENABLE_C_INTERFACE)
message(STATUS "Provide C89 interface")
endif()
if(ENABLE_JNI)
find_package(JNI REQUIRED)
if (JNI_FOUND)
message (STATUS "JNI_INCLUDE_DIRS=${JNI_INCLUDE_DIRS}")
message (STATUS "JNI_LIBRARIES=${JNI_LIBRARIES}")
endif()
endif()
add_subdirectory(include)
add_subdirectory(src)
if(ENABLE_UNIT_TESTS AND BCTOOLBOX_TESTER_FOUND)
......
This diff is collapsed.
......@@ -8,6 +8,14 @@ Lime can run the Signal Protocol using elliptic curve 25519 or curve 448-goldilo
It is designed to work jointly with *Linphone*[1] in a multiple devices per user and multiple users per device environment.
License
--------
Copyright (c) 2019 Belledonne Communications SARL under GNU GPLv3 (see *LICENSE.txt*)
The above license excludes the content of *src/jni* directory Copyright (c) 2016 Mapbox covered by its respective license,
see *src/jni/LICENSE.txt*
Dependencies
------------
- *bctoolbox[2]* : portability layer, built with Elliptic Curve Cryptography
......@@ -23,7 +31,6 @@ Build instrucitons
make install
```
Documentation
-------------
......@@ -38,23 +45,42 @@ Comprehensive documentation on implementation choices and built-in protocols in
Testing
-------
To test on local machine, you must run a local X3DH server.
To test on local machine, you must run a local X3DH server.
- A nodejs/sqlite version of UNSECURE X3DH server is provided in *tester/server/nodejs*
See README from this directory for instructions.
- A php/mysql (on docker) version of UNSECURE X3DH server is provided in *tester/server/php*
See README from this directory for instructions.
If a local X3DH server is running with default parameter just run
```
make test
```
To launch a particular test suite, use ctest, running:
```
ctest -R <Test suite name> [--verbose]
```
Test suite names are
- *crypto*: Test the crypto primitives
- *double_ratchet*: Specific to the Double Ratchet protocol
- *hello_world*: Basic Lime test, mostly a demo code. Requires a live X3DH server on localhost
- *lime*: Complete testing of all features provided by the library. Requires a live X3DH server on localhost
- *C_ffi*: Available only if C interface is enabled, test the C89 foreign function interface
- *JNI*: Available only if JNI is enabled, test the Java foreign function interface
Library settings
----------------
Some mostly harmless settings are available in *src/lime_settings.hpp*
Library API
Library APIs
-----------
The C++11 API is available in *include/lime/lime.hpp*
if enabled (see Options), a C89 FFI is provided by *include/lime/lime_ffi.h*
if enabled (see Options), a java FFI is provided by *Lime.jar* located in the build directory in *src/java* subdirectory
Options
-------
......@@ -69,6 +95,7 @@ Options
- `ENABLE_Curve448` : Enable support of Curve 448.
- `ENABLE_PROFILING` : Enable code profiling for GCC
- `ENABLE_C_INTERFACE` : Enable support of C89 foreign function interface
- `ENABLE_JNI` : Enable support of Java foreign function interface
------------------
......
......@@ -48,13 +48,18 @@ if (ENABLE_C_INTERFACE)
set(LIME_SOURCE_FILES_CXX ${LIME_SOURCE_FILES_CXX} lime_ffi.cpp)
endif()
if (ENABLE_JNI)
set(LIME_SOURCE_FILES_CXX ${LIME_SOURCE_FILES_CXX} lime_jni.cpp)
add_subdirectory(java)
endif()
bc_apply_compile_flags(LIME_SOURCE_FILES_CXX STRICT_OPTIONS_CPP STRICT_OPTIONS_CXX)
if(ENABLE_STATIC)
add_library(lime-static STATIC ${LIME_HEADER_FILES} ${LIME_SOURCE_FILES_CXX})
set_target_properties(lime-static PROPERTIES OUTPUT_NAME lime)
target_include_directories(lime-static PUBLIC ${BCTOOLBOX_INCLUDE_DIRS} ${SOCI_INCLUDE_DIRS} ${SOCI_INCLUDE_DIRS}/soci)
target_link_libraries(lime-static INTERFACE ${BCTOOLBOX_CORE_LIBRARIES} ${SOCI_LIBRARIES} ${SOCI_sqlite3_PLUGIN})
target_include_directories(lime-static PUBLIC ${BCTOOLBOX_INCLUDE_DIRS} ${SOCI_INCLUDE_DIRS} ${SOCI_INCLUDE_DIRS}/soci ${JNI_INCLUDE_DIRS})
target_link_libraries(lime-static INTERFACE ${BCTOOLBOX_CORE_LIBRARIES} ${SOCI_LIBRARIES} ${SOCI_sqlite3_PLUGIN} ${JNI_LIBRARIES})
if(ENABLE_PROFILING)
set_target_properties(lime-static PROPERTIES LINK_FLAGS "-pg")
endif()
......@@ -62,8 +67,8 @@ endif()
if(ENABLE_SHARED)
add_library(lime SHARED ${LIME_HEADER_FILES} ${LIME_SOURCE_FILES_CXX})
set_target_properties(lime PROPERTIES VERSION ${LIME_SO_VERSION})
target_include_directories(lime PUBLIC ${BCTOOLBOX_INCLUDE_DIRS} ${SOCI_INCLUDE_DIRS} ${SOCI_INCLUDE_DIRS}/soci)
target_link_libraries(lime PRIVATE ${BCTOOLBOX_CORE_LIBRARIES} ${SOCI_LIBRARIES} ${SOCI_sqlite3_PLUGIN})
target_include_directories(lime PUBLIC ${BCTOOLBOX_INCLUDE_DIRS} ${SOCI_INCLUDE_DIRS} ${SOCI_INCLUDE_DIRS}/soci ${JNI_INCLUDE_DIRS})
target_link_libraries(lime PRIVATE ${BCTOOLBOX_CORE_LIBRARIES} ${SOCI_LIBRARIES} ${SOCI_sqlite3_PLUGIN} ${JNI_LIBRARIES})
# TODO: replace by if(APPLE) when we want to make apple framework on linphone-desktop too
if(IOS)
if(IOS)
......
############################################################################
# CMakeLists.txt
# Copyright (C) 2019 Belledonne Communications, Grenoble France
#
############################################################################
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
############################################################################
find_package(Java REQUIRED)
include(UseJava)
set (LIME_SOURCE_FILES_JAVA
org/linphone/lime/LimeCurveId.java
org/linphone/lime/LimeEncryptionPolicy.java
org/linphone/lime/LimeOutputBuffer.java
org/linphone/lime/LimePeerDeviceStatus.java
org/linphone/lime/LimeCallbackReturn.java
org/linphone/lime/LimeStatusCallback.java
org/linphone/lime/RecipientData.java
org/linphone/lime/PostToX3DH.java
org/linphone/lime/LimeManager.java
)
add_jar(Lime ${LIME_SOURCE_FILES_JAVA})
get_target_property(Lime_jarFile Lime JAR_FILE)
message(STATUS "Lime Jar file ${Lime_jarFile}")
/*
LimeCallbackReturn.java
@author Johan Pascal
@copyright Copyright (C) 2019 Belledonne Communications SARL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.lime;
/** @brief Enumeration remapping the lime::CallbackReturn
* - java enumeration is mapped to an int
* - jni code map the same int to the original lime::CallbackReturn
*/
public enum LimeCallbackReturn {
SUCCESS(0),
FAIL(1);
private int native_val; /* Store the native(used by jni) integer value */
LimeCallbackReturn(int val) {
native_val = val;
}
/**
* @brief static method to get an enum value from the native jni integer(used as returned value by native function)
*
* @param[in] val integer value mapped to the jni enum
*
* @return one of the enumeration value, unknown input will silently default to FAIL
*/
public static LimeCallbackReturn fromNative(int val) {
switch (val) {
case 0:
return LimeCallbackReturn.SUCCESS;
case 1:
default:
return LimeCallbackReturn.FAIL;
}
}
}
/*
LimeCurveId.java
@author Johan Pascal
@copyright Copyright (C) 2019 Belledonne Communications SARL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.lime;
public enum LimeCurveId {
C25519(1), /**< Curve 25519 */
C448(2); /**< Curve 448-goldilocks */
private int native_val; /* Store the native(used by jni) integer value */
/**
* @brief get the native value (used to give input parameter values)
* @return the native value associated
*/
public int getNative() {return native_val;}
LimeCurveId(int val) {
native_val = val;
}
}
/*
LimeEncryptionPolicy.java
@author Johan Pascal
@copyright Copyright (C) 2019 Belledonne Communications SARL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.lime;
public enum LimeEncryptionPolicy {
DRMESSAGE(0), /**< the plaintext input is encrypted inside the Double Ratchet message (each recipient get a different encryption): not optimal for messages with numerous recipient */
CIPHERMESSAGE(1), /**< the plaintext input is encrypted with a random key and this random key is encrypted to each participant inside the Double Ratchet message(for a single recipient the overhead is 48 bytes) */
OPTIMIZEUPLOADSIZE(2), /**< optimize upload size: encrypt in DR message if plaintext is short enougth to beat the overhead introduced by cipher message scheme, otherwise use cipher message. Selection is made on upload size only. This is the default policy used */
OPTIMIZEGLOBALBANDWIDTH(3); /**< optimize bandwith usage: encrypt in DR message if plaintext is short enougth to beat the overhead introduced by cipher message scheme, otherwise use cipher message. Selection is made on uploadand download (from server to recipients) sizes added. */
private int native_val; /* Store the native(used by jni) integer value */
/**
* @brief get the native value (used to give input parameter values)
* @return the native value associated
*/
public int getNative() {return native_val;}
LimeEncryptionPolicy(int val) {
native_val = val;
}
}
This diff is collapsed.
/*
LimeOutputBuffer.java
@author Johan Pascal
@copyright Copyright (C) 2019 Belledonne Communications SARL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.lime;
/* encapsulate a byte buffer so this object is instanciated in java and passed as output parameter to the native function
* but the encapsulated byte buffer is created and populated by the native function */
public class LimeOutputBuffer {
public byte[] buffer;
}
/*
LimePeerDeviceStatus.java
@author Johan Pascal
@copyright Copyright (C) 2019 Belledonne Communications SARL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.lime;
/** @brief Enumeration remapping the lime::PeerDeviceStatus
* - java enumeration is mapped to an int
* - jni code map the same int to the original lime::PeerDeviceStatus
*/
public enum LimePeerDeviceStatus {
UNTRUSTED(0), /**< we know this device but do not trust it, that information shall be displayed to the end user, a colour code shall be enough */
TRUSTED(1), /**< this peer device already got its public identity key validated, that information shall be displayed to the end user too */
UNSAFE(2), /**< this status is a helper for the library user. It is used only by the peerDeviceStatus accessor functions */
FAIL(3), /**< when returned by decrypt : we could not decrypt the incoming message\n
when returned by encrypt in the peerStatus: we could not encrypt to this recipient(probably because it does not published keys on the X3DH server) */
UNKNOWN(4); /**< when returned after encryption or decryption, means it is the first time we communicate with this device (and thus create a DR session with it)\n
when returned by a get_peerDeviceStatus: this device is not in localStorage */
private int native_val; /* Store the native(used by jni) integer value */
/**
* @brief get the native value (used to give input parameter values)
* @return the native value associated
*/
public int getNative() {return native_val;}
/**
* @brief static method to get an enum value from the native jni integer(used as returned value by native function)
*
* @param[in] val integer value mapped to the jni enum
*
* @return one of the enumeration value, unknown input will silently default to UNKNOWN
*/
public static LimePeerDeviceStatus fromNative(int val) {
switch (val) {
case 0:
return LimePeerDeviceStatus.UNTRUSTED;
case 1:
return LimePeerDeviceStatus.TRUSTED;
case 2:
return LimePeerDeviceStatus.UNSAFE;
case 3:
return LimePeerDeviceStatus.FAIL;
case 4:
default:
return LimePeerDeviceStatus.UNKNOWN;
}
}
LimePeerDeviceStatus(int val) {
native_val = val;
}
}
/*
LimeStatusCallback.java
@author Johan Pascal
@copyright Copyright (C) 2019 Belledonne Communications SARL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.lime;
/** @brief Define an interface for the status callback
* The native code will call this callback function on
* the object passed as parameter to it.
*/
public interface LimeStatusCallback {
/**
* @brief Function called by native code when asynchronous processing is completed
*
* @param[in] status an integer mapped lime:CallbackReturn, use LimeCallbackReturn.fromNative to turn it into a java enumeration
* @param[in] message a string message giving some details in case of failure
*/
public void callback(int status, String message);
}
/*
PostToX3DH.java
@author Johan Pascal
@copyright Copyright (C) 2019 Belledonne Communications SARL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.lime;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
import java.io.DataOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
/** @brief Class to communicate with X3DH server
* an object is instanciated for each POST to the server as it hold a pointer to a encapsulated c++ closure
* the object is instanciated from the native code so do not modify class or method name
*/
public class PostToX3DH {
public long m_responseFunctionPtr; // store a native pointer used to send the response to the lime library via a stateful callback
/** @brief Function call by native side to post a message to an X3DH server.
* The connection is synchronous so this function also collect the answer
* and send it back to the native library using the process_response native method
*/
public void postToX3DHServer(long ptr, String url, String from, byte[] message) {
try {
m_responseFunctionPtr = ptr;
// connect to the given URL
String local_url = new String(url.toCharArray());
URL obj = new URL(local_url);
HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();
// set request header
con.setRequestMethod("POST");
con.setRequestProperty("User-Agent", "lime");
con.setRequestProperty("Content-type", "x3dh/octet-stream");
con.setRequestProperty("From", from);
// Send post request
con.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.write(message, 0, message.length);
wr.flush();
wr.close();
// wait for a response
int responseCode = con.getResponseCode();
InputStream in = con.getInputStream();
ByteArrayOutputStream response = new ByteArrayOutputStream( );
byte[] buffer = new byte[256];
int bufferLength;
while ((bufferLength = in.read(buffer)) != -1){
response.write(buffer, 0, bufferLength);
}
in.close();
// call response process native function
process_response(m_responseFunctionPtr, responseCode, response.toByteArray());
response.close();
}
catch (Exception e) {
System.out.println("Exception during HTTPS connection" + e.getMessage());
}
}
// native function to process response
public static native void process_response(long ptr, int responseCode, byte[] response);
// at construction(fired by native code only), store the native pointer(casted to a jlong)
// to be able to give it back to the native process_response method
public PostToX3DH() {
m_responseFunctionPtr = 0;
}
public PostToX3DH(long response) {
m_responseFunctionPtr = response;
}
}
/*
RecipientData.java
@author Johan Pascal
@copyright Copyright (C) 2019 Belledonne Communications SARL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.lime;
public class RecipientData {
public String deviceId;
private int peerStatus; // peer Status is stored in native enumeration, so native code can access it easily
public byte[] DRmessage;
public LimePeerDeviceStatus getPeerStatus() {
return LimePeerDeviceStatus.fromNative(peerStatus);
}
public void setPeerStatus(LimePeerDeviceStatus status) {
peerStatus = status.getNative();
}
public RecipientData(String p_deviceId) {
deviceId = p_deviceId;
peerStatus = LimePeerDeviceStatus.UNKNOWN.getNative(); // default to unknown
}
public RecipientData(String p_deviceId, LimePeerDeviceStatus status) {
deviceId = p_deviceId;
peerStatus = status.getNative();
}
}
Copyright © 2016, Mapbox
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
#pragma once
#include <jni/functions.hpp>
namespace jni
{
// A deleter that gets the JNIEnv via GetEnv, rather than storing the value passed to the constructor.
// The deleting thread must have a JVM attachment.
//
// Useful when deletion will happen on an auxiliary thread, particularly the finalizer thread. In such
// cases, you may use one of the following:
//
// low-level: UniqueGlobalRef<jobject, EnvGettingDeleter> and NewGlobalRef<EnvGettingDeleter>
// high-level: Global<Object<Tag>, EnvGettingDeleter> and obj.NewGlobalRef<EnvGettingDeleter>
//
template < RefDeletionMethod DeleteRef >
class EnvGettingDeleter
{
private:
JavaVM* vm = nullptr;
public:
EnvGettingDeleter() = default;
EnvGettingDeleter(JNIEnv& e) : vm(&GetJavaVM(e)) {}
void operator()(jobject* p) const
{
if (p)
{
assert(vm);
(GetEnv(*vm).*DeleteRef)(Unwrap(p));
}
}
};
// A deleter that first tries GetEnv, but falls back to AttachCurrentThread if a JVM is not already attached.
// In the latter case, it detaches after deleting the reference.
//
// Useful when deletion will happen on an auxiliary thread which may or may not have a JVM attachment. In such
// cases, you may use one of the following:
//
// low-level: UniqueGlobalRef<jobject, EnvAttachingDeleter> and NewGlobalRef<EnvAttachingDeleter>
// high-level: Global<Object<Tag>, EnvAttachingDeleter> and obj.NewGlobalRef<EnvAttachingDeleter>
//
template < RefDeletionMethod DeleteRef >
class EnvAttachingDeleter
{
private:
JavaVM* vm = nullptr;
public:
EnvAttachingDeleter() = default;
EnvAttachingDeleter(JNIEnv& e) : vm(&GetJavaVM(e)) {}
void operator()(jobject* p) const
{
if (p)
{
assert(vm);
JNIEnv* env = nullptr;
jint err = vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_1);
if (err == JNI_OK)
{
(env->*DeleteRef)(Unwrap(p));
}
else if (err == JNI_EDETACHED)
{
((*AttachCurrentThread(*vm)).*DeleteRef)(Unwrap(p));
}
else
{
CheckErrorCode(err);
}
}
}
};
// A deleter that tries to get the JNIEnv via GetEnv, and does nothing if that fails.
//
// This is used to ignore GlobalRef deletions that happen after a thread has been detached,
// for instance during process shutdown, when there's no need to release the reference anyway.
// Specifically, it's what Class<T>::Singleton uses.
//
template < RefDeletionMethod DeleteRef >
class EnvIgnoringDeleter
{
private:
JavaVM* vm = nullptr;
public:
EnvIgnoringDeleter() = default;
EnvIgnoringDeleter(JNIEnv& e) : vm(&GetJavaVM(e)) {}
void operator()(jobject* p) const
{
if (p)
{
assert(vm);
JNIEnv* env = nullptr;
jint err = vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_1);
if (err == JNI_OK)
{
(env->*DeleteRef)(Unwrap(p));
}
else if (err != JNI_EDETACHED)
{
CheckErrorCode(err);
}
}
}
};
}