From e51a0a8bf161c6aaaa28f21924e53465ecc3e02c Mon Sep 17 00:00:00 2001 From: Tommi Date: Tue, 27 Feb 2018 15:30:29 +0100 Subject: [PATCH] Re-Reland "Some cleanup for the logging code:"" This is a reland of 12dc1842d62ee8df1e462f9b6a617fef9ab8b3b7. Original change's description: > Some cleanup for the logging code: > > * Only include 'tag' for Android. Before there was an > extra std::string variable per log statement for all > platforms. > * Remove unused logging macro for Windows and 'module' ctor argument. > * Move httpcommon code out of logging and to where it's used. > > Change-Id: I347805d3df2cee2840c0d2eef5bfefaff1cdbf37 > Bug: webrtc:8928 > Reviewed-on: https://webrtc-review.googlesource.com/57183 > Reviewed-by: Jonas Olsson > Reviewed-by: Karl Wiberg > Commit-Queue: Tommi > Cr-Commit-Position: refs/heads/master@{#22184} Bug: webrtc:8928 Change-Id: Ib97895aaeb376e19f136d258c0259a340235a5d1 Reviewed-on: https://webrtc-review.googlesource.com/58200 Commit-Queue: Tommi Reviewed-by: Karl Wiberg Cr-Commit-Position: refs/heads/master@{#22208} --- rtc_base/BUILD.gn | 5 +- rtc_base/httpcommon.cc | 85 ++++++++++++++++++++++++--- rtc_base/logging.cc | 97 ++++++++++++------------------- rtc_base/logging.h | 70 +++++++++++----------- rtc_base/socketadapters.cc | 1 - rtc_base/win32securityerrors.cc | 49 ---------------- sdk/android/src/jni/pc/logging.cc | 3 +- 7 files changed, 155 insertions(+), 155 deletions(-) delete mode 100644 rtc_base/win32securityerrors.cc diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index 827cbb4b63..df4969fdd9 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -285,6 +285,10 @@ rtc_source_set("rtc_base_approved_generic") { "logging.cc", "logging.h", ] + + # logging.h needs the deprecation header while downstream projects are + # removing code that depends on logging implementation details. + deps += [ ":deprecation" ] } if (is_component_build && is_win) { # Copy the VS runtime DLLs into the isolate so that they don't have to be @@ -765,7 +769,6 @@ rtc_static_library("rtc_base_generic") { "win32.h", "win32filesystem.cc", "win32filesystem.h", - "win32securityerrors.cc", "win32window.cc", "win32window.h", ] diff --git a/rtc_base/httpcommon.cc b/rtc_base/httpcommon.cc index 345b4aa28b..e60ad477a8 100644 --- a/rtc_base/httpcommon.cc +++ b/rtc_base/httpcommon.cc @@ -11,7 +11,6 @@ #include #if defined(WEBRTC_WIN) -#define WIN32_LEAN_AND_MEAN #include #include #include @@ -31,10 +30,80 @@ #include "rtc_base/socketaddress.h" namespace rtc { - +namespace { #if defined(WEBRTC_WIN) -extern const ConstantLabel SECURITY_ERRORS[]; -#endif +/////////////////////////////////////////////////////////////////////////////// +// ConstantToLabel can be used to easily generate string names from constant +// values. This can be useful for logging descriptive names of error messages. +// Usage: +// const ConstantToLabel LIBRARY_ERRORS[] = { +// KLABEL(SOME_ERROR), +// KLABEL(SOME_OTHER_ERROR), +// ... +// LASTLABEL +// } +// +// int err = LibraryFunc(); +// LOG(LS_ERROR) << "LibraryFunc returned: " +// << GetErrorName(err, LIBRARY_ERRORS); +struct ConstantToLabel { int value; const char * label; }; + +const char* LookupLabel(int value, const ConstantToLabel entries[]) { + for (int i = 0; entries[i].label; ++i) { + if (value == entries[i].value) { + return entries[i].label; + } + } + return 0; +} + +std::string GetErrorName(int err, const ConstantToLabel* err_table) { + if (err == 0) + return "No error"; + + if (err_table != 0) { + if (const char* value = LookupLabel(err, err_table)) + return value; + } + + char buffer[16]; + snprintf(buffer, sizeof(buffer), "0x%08x", err); + return buffer; +} + +#define KLABEL(x) { x, #x } +#define LASTLABEL { 0, 0 } + +const ConstantToLabel SECURITY_ERRORS[] = { + KLABEL(SEC_I_COMPLETE_AND_CONTINUE), + KLABEL(SEC_I_COMPLETE_NEEDED), + KLABEL(SEC_I_CONTEXT_EXPIRED), + KLABEL(SEC_I_CONTINUE_NEEDED), + KLABEL(SEC_I_INCOMPLETE_CREDENTIALS), + KLABEL(SEC_I_RENEGOTIATE), + KLABEL(SEC_E_CERT_EXPIRED), + KLABEL(SEC_E_INCOMPLETE_MESSAGE), + KLABEL(SEC_E_INSUFFICIENT_MEMORY), + KLABEL(SEC_E_INTERNAL_ERROR), + KLABEL(SEC_E_INVALID_HANDLE), + KLABEL(SEC_E_INVALID_TOKEN), + KLABEL(SEC_E_LOGON_DENIED), + KLABEL(SEC_E_NO_AUTHENTICATING_AUTHORITY), + KLABEL(SEC_E_NO_CREDENTIALS), + KLABEL(SEC_E_NOT_OWNER), + KLABEL(SEC_E_OK), + KLABEL(SEC_E_SECPKG_NOT_FOUND), + KLABEL(SEC_E_TARGET_UNKNOWN), + KLABEL(SEC_E_UNKNOWN_CREDENTIALS), + KLABEL(SEC_E_UNSUPPORTED_FUNCTION), + KLABEL(SEC_E_UNTRUSTED_ROOT), + KLABEL(SEC_E_WRONG_PRINCIPAL), + LASTLABEL +}; +#undef KLABEL +#undef LASTLABEL +#endif // defined(WEBRTC_WIN) +} // namespace ////////////////////////////////////////////////////////////////////// // Enum - TODO: expose globally later? @@ -865,7 +934,7 @@ HttpAuthResult HttpAuthenticate( ret = InitializeSecurityContextA(&neg->cred, &neg->ctx, spn, flags, 0, SECURITY_NATIVE_DREP, &in_buf_desc, 0, &neg->ctx, &out_buf_desc, &ret_flags, &lifetime); if (FAILED(ret)) { RTC_LOG(LS_ERROR) << "InitializeSecurityContext returned: " - << ErrorName(ret, SECURITY_ERRORS); + << GetErrorName(ret, SECURITY_ERRORS); return HAR_ERROR; } } else if (neg->specified_credentials) { @@ -931,7 +1000,7 @@ HttpAuthResult HttpAuthenticate( SECPKG_CRED_OUTBOUND, 0, pauth_id, 0, 0, &cred, &lifetime); if (ret != SEC_E_OK) { RTC_LOG(LS_ERROR) << "AcquireCredentialsHandle error: " - << ErrorName(ret, SECURITY_ERRORS); + << GetErrorName(ret, SECURITY_ERRORS); return HAR_IGNORE; } @@ -941,7 +1010,7 @@ HttpAuthResult HttpAuthenticate( ret = InitializeSecurityContextA(&cred, 0, spn, flags, 0, SECURITY_NATIVE_DREP, 0, 0, &ctx, &out_buf_desc, &ret_flags, &lifetime); if (FAILED(ret)) { RTC_LOG(LS_ERROR) << "InitializeSecurityContext returned: " - << ErrorName(ret, SECURITY_ERRORS); + << GetErrorName(ret, SECURITY_ERRORS); FreeCredentialsHandle(&cred); return HAR_IGNORE; } @@ -955,7 +1024,7 @@ HttpAuthResult HttpAuthenticate( if ((ret == SEC_I_COMPLETE_NEEDED) || (ret == SEC_I_COMPLETE_AND_CONTINUE)) { ret = CompleteAuthToken(&neg->ctx, &out_buf_desc); RTC_LOG(LS_VERBOSE) << "CompleteAuthToken returned: " - << ErrorName(ret, SECURITY_ERRORS); + << GetErrorName(ret, SECURITY_ERRORS); if (FAILED(ret)) { return HAR_ERROR; } diff --git a/rtc_base/logging.cc b/rtc_base/logging.cc index fa84e17896..4798f4dbec 100644 --- a/rtc_base/logging.cc +++ b/rtc_base/logging.cc @@ -29,8 +29,6 @@ static const int kMaxLogLineSize = 1024 - 60; #endif // WEBRTC_MAC && !defined(WEBRTC_IOS) || WEBRTC_ANDROID -static const char kLibjingle[] = "libjingle"; - #include #include @@ -41,7 +39,7 @@ static const char kLibjingle[] = "libjingle"; #include "rtc_base/criticalsection.h" #include "rtc_base/logging.h" -#include "rtc_base/platform_thread.h" +#include "rtc_base/platform_thread_types.h" #include "rtc_base/stringencode.h" #include "rtc_base/strings/string_builder.h" #include "rtc_base/stringutils.h" @@ -82,32 +80,6 @@ std::ostream& GetNoopStream() { CriticalSection g_log_crit; } // namespace -///////////////////////////////////////////////////////////////////////////// -// Constant Labels -///////////////////////////////////////////////////////////////////////////// - -const char* FindLabel(int value, const ConstantLabel entries[]) { - for (int i = 0; entries[i].label; ++i) { - if (value == entries[i].value) { - return entries[i].label; - } - } - return 0; -} - -std::string ErrorName(int err, const ConstantLabel* err_table) { - if (err == 0) - return "No error"; - - if (err_table != 0) { - if (const char* value = FindLabel(err, err_table)) - return value; - } - - char buffer[16]; - snprintf(buffer, sizeof(buffer), "0x%08x", err); - return buffer; -} ///////////////////////////////////////////////////////////////////////////// // LogMessage @@ -128,9 +100,8 @@ LogMessage::LogMessage(const char* file, int line, LoggingSeverity sev, LogErrorContext err_ctx, - int err, - const char* module) - : severity_(sev), tag_(kLibjingle), is_noop_(IsNoop(sev)) { + int err) + : severity_(sev), is_noop_(IsNoop(sev)) { // If there's no need to do any work, let's not :) if (is_noop_) return; @@ -165,12 +136,10 @@ LogMessage::LogMessage(const char* file, #ifdef WEBRTC_WIN case ERRCTX_HRESULT: { char msgbuf[256]; - DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM; - HMODULE hmod = GetModuleHandleA(module); - if (hmod) - flags |= FORMAT_MESSAGE_FROM_HMODULE; + DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS; if (DWORD len = FormatMessageA( - flags, hmod, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + flags, nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), msgbuf, sizeof(msgbuf) / sizeof(msgbuf[0]), nullptr)) { while ((len > 0) && isspace(static_cast(msgbuf[len-1]))) { @@ -195,21 +164,32 @@ LogMessage::LogMessage(const char* file, } } +#if defined(WEBRTC_ANDROID) LogMessage::LogMessage(const char* file, int line, LoggingSeverity sev, - const std::string& tag) + const char* tag) : LogMessage(file, line, sev, ERRCTX_NONE, - 0 /* err */, - nullptr /* module */) { + 0 /* err */) { if (!is_noop_) { tag_ = tag; print_stream_ << tag << ": "; } } +#endif + +// DEPRECATED. Currently only used by downstream projects that use +// implementation details of logging.h. Work is ongoing to remove those +// dependencies. +LogMessage::LogMessage(const char* file, int line, LoggingSeverity sev, + const std::string& tag) + : LogMessage(file, line, sev) { + if (!is_noop_) + print_stream_ << tag << ": "; +} LogMessage::~LogMessage() { if (is_noop_) @@ -224,7 +204,11 @@ LogMessage::~LogMessage() { const std::string str = print_stream_.str(); if (severity_ >= g_dbg_sev) { +#if defined(WEBRTC_ANDROID) OutputToDebug(str, severity_, tag_); +#else + OutputToDebug(str, severity_); +#endif } CritScope cs(&g_log_crit); @@ -349,19 +333,8 @@ void LogMessage::ConfigureLogging(const char* params) { // from the command line, we'll see the output there. Otherwise, create // our own console window. // Note: These methods fail if a console already exists, which is fine. - bool success = false; - typedef BOOL (WINAPI* PFN_AttachConsole)(DWORD); - if (HINSTANCE kernel32 = ::LoadLibrary(L"kernel32.dll")) { - // AttachConsole is defined on WinXP+. - if (PFN_AttachConsole attach_console = reinterpret_cast - (::GetProcAddress(kernel32, "AttachConsole"))) { - success = (FALSE != attach_console(ATTACH_PARENT_PROCESS)); - } - ::FreeLibrary(kernel32); - } - if (!success) { + if (!AttachConsole(ATTACH_PARENT_PROCESS)) ::AllocConsole(); - } } #endif // WEBRTC_WIN @@ -377,9 +350,14 @@ void LogMessage::UpdateMinLogSeverity() g_min_sev = min_sev; } +#if defined(WEBRTC_ANDROID) void LogMessage::OutputToDebug(const std::string& str, LoggingSeverity severity, - const std::string& tag) { + const char* tag) { +#else +void LogMessage::OutputToDebug(const std::string& str, + LoggingSeverity severity) { +#endif bool log_to_stderr = log_to_stderr_; #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) && defined(NDEBUG) // On the Mac, all stderr output goes to the Console log and causes clutter. @@ -400,7 +378,8 @@ void LogMessage::OutputToDebug(const std::string& str, if (key != nullptr) { CFRelease(key); } -#endif +#endif // defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) && defined(NDEBUG) + #if defined(WEBRTC_WIN) // Always log to the debugger. // Perhaps stderr should be controlled by a preference, as on Mac? @@ -415,6 +394,7 @@ void LogMessage::OutputToDebug(const std::string& str, } } #endif // WEBRTC_WIN + #if defined(WEBRTC_ANDROID) // Android's logging facility uses severity to log messages but we // need to map libjingle's severity levels to Android ones first. @@ -423,7 +403,7 @@ void LogMessage::OutputToDebug(const std::string& str, int prio; switch (severity) { case LS_SENSITIVE: - __android_log_write(ANDROID_LOG_INFO, tag.c_str(), "SENSITIVE"); + __android_log_write(ANDROID_LOG_INFO, tag, "SENSITIVE"); if (log_to_stderr) { fprintf(stderr, "SENSITIVE"); fflush(stderr); @@ -450,15 +430,14 @@ void LogMessage::OutputToDebug(const std::string& str, int idx = 0; const int max_lines = size / kMaxLogLineSize + 1; if (max_lines == 1) { - __android_log_print(prio, tag.c_str(), "%.*s", size, str.c_str()); + __android_log_print(prio, tag, "%.*s", size, str.c_str()); } else { while (size > 0) { const int len = std::min(size, kMaxLogLineSize); // Use the size of the string in the format (str may have \0 in the // middle). - __android_log_print(prio, tag.c_str(), "[%d/%d] %.*s", - line + 1, max_lines, - len, str.c_str() + idx); + __android_log_print(prio, tag, "[%d/%d] %.*s", line + 1, max_lines, len, + str.c_str() + idx); idx += len; size -= len; ++line; diff --git a/rtc_base/logging.h b/rtc_base/logging.h index 9a2fd1aab7..efaadc5af1 100644 --- a/rtc_base/logging.h +++ b/rtc_base/logging.h @@ -26,9 +26,8 @@ // RTC_LOG_F(sev) Like RTC_LOG(), but includes the name of the current function. // RTC_LOG_T(sev) Like RTC_LOG(), but includes the this pointer. // RTC_LOG_T_F(sev) Like RTC_LOG_F(), but includes the this pointer. -// RTC_LOG_GLE(M)(sev [, mod]) attempt to add a string description of the -// HRESULT returned by GetLastError. The "M" variant allows searching of a -// DLL's string table for the error description. +// RTC_LOG_GLE(sev [, mod]) attempt to add a string description of the +// HRESULT returned by GetLastError. // RTC_LOG_ERRNO(sev) attempts to add a string description of an errno-derived // error. errno and associated facilities exist on both Windows and POSIX, // but on Windows they only apply to the C/C++ runtime. @@ -58,6 +57,7 @@ #include "rtc_base/basictypes.h" #include "rtc_base/constructormagic.h" +#include "rtc_base/deprecation.h" #include "rtc_base/thread_annotations.h" #if !defined(NDEBUG) || defined(DLOG_ALWAYS_ON) @@ -68,29 +68,6 @@ namespace rtc { -/////////////////////////////////////////////////////////////////////////////// -// ConstantLabel can be used to easily generate string names from constant -// values. This can be useful for logging descriptive names of error messages. -// Usage: -// const ConstantLabel LIBRARY_ERRORS[] = { -// KLABEL(SOME_ERROR), -// KLABEL(SOME_OTHER_ERROR), -// ... -// LASTLABEL -// } -// -// int err = LibraryFunc(); -// LOG(LS_ERROR) << "LibraryFunc returned: " -// << ErrorName(err, LIBRARY_ERRORS); - -struct ConstantLabel { int value; const char * label; }; -#define KLABEL(x) { x, #x } -#define TLABEL(x, y) { x, y } -#define LASTLABEL { 0, 0 } - -const char* FindLabel(int value, const ConstantLabel entries[]); -std::string ErrorName(int err, const ConstantLabel* err_table); - #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) // Returns a UTF8 description from an OS X Status error. std::string DescriptionFromOSStatus(OSStatus err); @@ -148,12 +125,17 @@ class LogMessage { int line, LoggingSeverity sev, LogErrorContext err_ctx = ERRCTX_NONE, - int err = 0, - const char* module = nullptr); + int err = 0); - LogMessage(const char* file, - int line, - LoggingSeverity sev, +#if defined(WEBRTC_ANDROID) + LogMessage(const char* file, int line, LoggingSeverity sev, const char* tag); +#endif + + // DEPRECATED - DO NOT USE - PLEASE USE THE MACROS INSTEAD OF THE CLASS. + // Android code should use the 'const char*' version since tags are static + // and we want to avoid allocating a std::string copy per log line. + RTC_DEPRECATED + LogMessage(const char* file, int line, LoggingSeverity sev, const std::string& tag); ~LogMessage(); @@ -214,9 +196,13 @@ class LogMessage { static void UpdateMinLogSeverity(); // These write out the actual log messages. +#if defined(WEBRTC_ANDROID) static void OutputToDebug(const std::string& msg, LoggingSeverity severity, - const std::string& tag); + const char* tag); +#else + static void OutputToDebug(const std::string& msg, LoggingSeverity severity); +#endif // Checks the current global debug severity and if the |streams_| collection // is empty. If |severity| is smaller than the global severity and if the @@ -234,8 +220,10 @@ class LogMessage { // The severity level of this message LoggingSeverity severity_; +#if defined(WEBRTC_ANDROID) // The Android debug output tag. - std::string tag_; + const char* tag_ = "libjingle"; +#endif // String data generated in the constructor, that should be appended to // the message before output. @@ -326,8 +314,6 @@ inline bool LogCheckLevel(LoggingSeverity sev) { RTC_LOG_E(sev, HRESULT, err) #define RTC_LOG_GLE(sev) \ RTC_LOG_GLE_EX(sev, GetLastError()) -#define RTC_LOG_GLEM(sev, mod) \ - RTC_LOG_E(sev, HRESULT, GetLastError(), mod) #define RTC_LOG_ERR_EX(sev, err) \ RTC_LOG_GLE_EX(sev, err) #define RTC_LOG_ERR(sev) \ @@ -344,9 +330,21 @@ inline bool LogCheckLevel(LoggingSeverity sev) { RTC_LOG_ERRNO(sev) #endif // WEBRTC_WIN +#if defined(WEBRTC_ANDROID) +namespace internal { +// Inline adapters provided for backwards compatibility for downstream projects. +inline const char* AdaptString(const char* str) { return str; } +inline const char* AdaptString(const std::string& str) { return str.c_str(); } +} // namespace internal #define RTC_LOG_TAG(sev, tag) \ RTC_LOG_SEVERITY_PRECONDITION(sev) \ - rtc::LogMessage(nullptr, 0, sev, tag).stream() + rtc::LogMessage(nullptr, 0, sev, rtc::internal::AdaptString(tag)).stream() +#else +// DEPRECATED. This macro is only intended for Android. +#define RTC_LOG_TAG(sev, tag) \ + RTC_LOG_SEVERITY_PRECONDITION(sev) \ + rtc::LogMessage(nullptr, 0, sev).stream() +#endif // The RTC_DLOG macros are equivalent to their RTC_LOG counterparts except that // they only generate code in debug builds. diff --git a/rtc_base/socketadapters.cc b/rtc_base/socketadapters.cc index 48e7898d60..814f23b290 100644 --- a/rtc_base/socketadapters.cc +++ b/rtc_base/socketadapters.cc @@ -16,7 +16,6 @@ #include #if defined(WEBRTC_WIN) -#define WIN32_LEAN_AND_MEAN #include #include #include diff --git a/rtc_base/win32securityerrors.cc b/rtc_base/win32securityerrors.cc deleted file mode 100644 index 2f8530a6bc..0000000000 --- a/rtc_base/win32securityerrors.cc +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2004 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "rtc_base/logging.h" -#include "rtc_base/win32.h" - -namespace rtc { - -/////////////////////////////////////////////////////////////////////////////// - -extern const ConstantLabel SECURITY_ERRORS[]; - -const ConstantLabel SECURITY_ERRORS[] = { - KLABEL(SEC_I_COMPLETE_AND_CONTINUE), - KLABEL(SEC_I_COMPLETE_NEEDED), - KLABEL(SEC_I_CONTEXT_EXPIRED), - KLABEL(SEC_I_CONTINUE_NEEDED), - KLABEL(SEC_I_INCOMPLETE_CREDENTIALS), - KLABEL(SEC_I_RENEGOTIATE), - KLABEL(SEC_E_CERT_EXPIRED), - KLABEL(SEC_E_INCOMPLETE_MESSAGE), - KLABEL(SEC_E_INSUFFICIENT_MEMORY), - KLABEL(SEC_E_INTERNAL_ERROR), - KLABEL(SEC_E_INVALID_HANDLE), - KLABEL(SEC_E_INVALID_TOKEN), - KLABEL(SEC_E_LOGON_DENIED), - KLABEL(SEC_E_NO_AUTHENTICATING_AUTHORITY), - KLABEL(SEC_E_NO_CREDENTIALS), - KLABEL(SEC_E_NOT_OWNER), - KLABEL(SEC_E_OK), - KLABEL(SEC_E_SECPKG_NOT_FOUND), - KLABEL(SEC_E_TARGET_UNKNOWN), - KLABEL(SEC_E_UNKNOWN_CREDENTIALS), - KLABEL(SEC_E_UNSUPPORTED_FUNCTION), - KLABEL(SEC_E_UNTRUSTED_ROOT), - KLABEL(SEC_E_WRONG_PRINCIPAL), - LASTLABEL -}; - -/////////////////////////////////////////////////////////////////////////////// - -} // namespace rtc diff --git a/sdk/android/src/jni/pc/logging.cc b/sdk/android/src/jni/pc/logging.cc index 48d400081c..4fb8215b1e 100644 --- a/sdk/android/src/jni/pc/logging.cc +++ b/sdk/android/src/jni/pc/logging.cc @@ -51,7 +51,8 @@ JNI_FUNCTION_DECLARATION(void, jstring j_message) { std::string message = JavaToStdString(jni, JavaParamRef(j_message)); std::string tag = JavaToStdString(jni, JavaParamRef(j_tag)); - RTC_LOG_TAG(static_cast(j_severity), tag) << message; + RTC_LOG_TAG(static_cast(j_severity), tag.c_str()) + << message; } } // namespace jni