tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ffjni.c (12300B)


      1 /*
      2 * JNI utility functions
      3 *
      4 * Copyright (c) 2015-2016 Matthieu Bouron <matthieu.bouron stupeflix.com>
      5 *
      6 * This file is part of FFmpeg.
      7 *
      8 * FFmpeg is free software; you can redistribute it and/or
      9 * modify it under the terms of the GNU Lesser General Public
     10 * License as published by the Free Software Foundation; either
     11 * version 2.1 of the License, or (at your option) any later version.
     12 *
     13 * FFmpeg is distributed in the hope that it will be useful,
     14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16 * Lesser General Public License for more details.
     17 *
     18 * You should have received a copy of the GNU Lesser General Public
     19 * License along with FFmpeg; if not, write to the Free Software
     20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
     21 */
     22 
     23 #include <jni.h>
     24 #include <pthread.h>
     25 #include <stdlib.h>
     26 
     27 #include "libavutil/bprint.h"
     28 #include "libavutil/error.h"
     29 #include "libavutil/log.h"
     30 #include "libavutil/mem.h"
     31 
     32 #include "config.h"
     33 #include "fffjni.h"
     34 #include "ffjni.h"
     35 
     36 static JavaVM *java_vm;
     37 static pthread_key_t current_env;
     38 static pthread_once_t once = PTHREAD_ONCE_INIT;
     39 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
     40 
     41 static void jni_detach_env(void *data)
     42 {
     43    if (java_vm) {
     44        (*java_vm)->DetachCurrentThread(java_vm);
     45    }
     46 }
     47 
     48 static void jni_create_pthread_key(void)
     49 {
     50    pthread_key_create(&current_env, jni_detach_env);
     51 }
     52 
     53 JNIEnv *ff_jni_get_env(void *log_ctx)
     54 {
     55    int ret = 0;
     56    JNIEnv *env = NULL;
     57 
     58    pthread_mutex_lock(&lock);
     59    if (java_vm == NULL) {
     60        java_vm = av_jni_get_java_vm(log_ctx);
     61    }
     62 
     63    if (!java_vm) {
     64        av_log(log_ctx, AV_LOG_ERROR, "No Java virtual machine has been registered\n");
     65        goto done;
     66    }
     67 
     68    pthread_once(&once, jni_create_pthread_key);
     69 
     70    if ((env = pthread_getspecific(current_env)) != NULL) {
     71        goto done;
     72    }
     73 
     74    ret = (*java_vm)->GetEnv(java_vm, (void **)&env, JNI_VERSION_1_6);
     75    switch(ret) {
     76    case JNI_EDETACHED:
     77        if ((*java_vm)->AttachCurrentThread(java_vm, &env, NULL) != 0) {
     78            av_log(log_ctx, AV_LOG_ERROR, "Failed to attach the JNI environment to the current thread\n");
     79            env = NULL;
     80        } else {
     81            pthread_setspecific(current_env, env);
     82        }
     83        break;
     84    case JNI_OK:
     85        break;
     86    case JNI_EVERSION:
     87        av_log(log_ctx, AV_LOG_ERROR, "The specified JNI version is not supported\n");
     88        break;
     89    default:
     90        av_log(log_ctx, AV_LOG_ERROR, "Failed to get the JNI environment attached to this thread\n");
     91        break;
     92    }
     93 
     94 done:
     95    pthread_mutex_unlock(&lock);
     96    return env;
     97 }
     98 
     99 char *ff_jni_jstring_to_utf_chars(JNIEnv *env, jstring string, void *log_ctx)
    100 {
    101    char *ret = NULL;
    102    const char *utf_chars = NULL;
    103 
    104    jboolean copy = 0;
    105 
    106    if (!string) {
    107        return NULL;
    108    }
    109 
    110    utf_chars = (*env)->GetStringUTFChars(env, string, &copy);
    111    if ((*env)->ExceptionCheck(env)) {
    112        (*env)->ExceptionClear(env);
    113        av_log(log_ctx, AV_LOG_ERROR, "String.getStringUTFChars() threw an exception\n");
    114        return NULL;
    115    }
    116 
    117    ret = av_strdup(utf_chars);
    118 
    119    (*env)->ReleaseStringUTFChars(env, string, utf_chars);
    120    if ((*env)->ExceptionCheck(env)) {
    121        (*env)->ExceptionClear(env);
    122        av_log(log_ctx, AV_LOG_ERROR, "String.releaseStringUTFChars() threw an exception\n");
    123        return NULL;
    124    }
    125 
    126    return ret;
    127 }
    128 
    129 jstring ff_jni_utf_chars_to_jstring(JNIEnv *env, const char *utf_chars, void *log_ctx)
    130 {
    131    jstring ret;
    132 
    133    ret = (*env)->NewStringUTF(env, utf_chars);
    134    if ((*env)->ExceptionCheck(env)) {
    135        (*env)->ExceptionClear(env);
    136        av_log(log_ctx, AV_LOG_ERROR, "NewStringUTF() threw an exception\n");
    137        return NULL;
    138    }
    139 
    140    return ret;
    141 }
    142 
    143 int ff_jni_exception_get_summary(JNIEnv *env, jthrowable exception, char **error, void *log_ctx)
    144 {
    145    int ret = 0;
    146 
    147    AVBPrint bp;
    148 
    149    char *name = NULL;
    150    char *message = NULL;
    151 
    152    jclass class_class = NULL;
    153    jmethodID get_name_id = NULL;
    154 
    155    jclass exception_class = NULL;
    156    jmethodID get_message_id = NULL;
    157 
    158    jstring string = NULL;
    159 
    160    av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
    161 
    162    exception_class = (*env)->GetObjectClass(env, exception);
    163    if ((*env)->ExceptionCheck(env)) {
    164        (*env)->ExceptionClear(env);
    165        av_log(log_ctx, AV_LOG_ERROR, "Could not find Throwable class\n");
    166        ret = AVERROR_EXTERNAL;
    167        goto done;
    168    }
    169 
    170    class_class = (*env)->GetObjectClass(env, exception_class);
    171    if ((*env)->ExceptionCheck(env)) {
    172        (*env)->ExceptionClear(env);
    173        av_log(log_ctx, AV_LOG_ERROR, "Could not find Throwable class's class\n");
    174        ret = AVERROR_EXTERNAL;
    175        goto done;
    176    }
    177 
    178    get_name_id = (*env)->GetMethodID(env, class_class, "getName", "()Ljava/lang/String;");
    179    if ((*env)->ExceptionCheck(env)) {
    180        (*env)->ExceptionClear(env);
    181        av_log(log_ctx, AV_LOG_ERROR, "Could not find method Class.getName()\n");
    182        ret = AVERROR_EXTERNAL;
    183        goto done;
    184    }
    185 
    186    string = (*env)->CallObjectMethod(env, exception_class, get_name_id);
    187    if ((*env)->ExceptionCheck(env)) {
    188        (*env)->ExceptionClear(env);
    189        av_log(log_ctx, AV_LOG_ERROR, "Class.getName() threw an exception\n");
    190        ret = AVERROR_EXTERNAL;
    191        goto done;
    192    }
    193 
    194    if (string) {
    195        name = ff_jni_jstring_to_utf_chars(env, string, log_ctx);
    196        (*env)->DeleteLocalRef(env, string);
    197        string = NULL;
    198    }
    199 
    200    get_message_id = (*env)->GetMethodID(env, exception_class, "getMessage", "()Ljava/lang/String;");
    201    if ((*env)->ExceptionCheck(env)) {
    202        (*env)->ExceptionClear(env);
    203        av_log(log_ctx, AV_LOG_ERROR, "Could not find method java/lang/Throwable.getMessage()\n");
    204        ret = AVERROR_EXTERNAL;
    205        goto done;
    206    }
    207 
    208    string = (*env)->CallObjectMethod(env, exception, get_message_id);
    209    if ((*env)->ExceptionCheck(env)) {
    210        (*env)->ExceptionClear(env);
    211        av_log(log_ctx, AV_LOG_ERROR, "Throwable.getMessage() threw an exception\n");
    212        ret = AVERROR_EXTERNAL;
    213        goto done;
    214    }
    215 
    216    if (string) {
    217        message = ff_jni_jstring_to_utf_chars(env, string, log_ctx);
    218        (*env)->DeleteLocalRef(env, string);
    219        string = NULL;
    220    }
    221 
    222    if (name && message) {
    223        av_bprintf(&bp, "%s: %s", name, message);
    224    } else if (name && !message) {
    225        av_bprintf(&bp, "%s occurred", name);
    226    } else if (!name && message) {
    227        av_bprintf(&bp, "Exception: %s", message);
    228    } else {
    229        av_log(log_ctx, AV_LOG_WARNING, "Could not retrieve exception name and message\n");
    230        av_bprintf(&bp, "Exception occurred");
    231    }
    232 
    233    ret = av_bprint_finalize(&bp, error);
    234 done:
    235 
    236    av_free(name);
    237    av_free(message);
    238 
    239    (*env)->DeleteLocalRef(env, class_class);
    240    (*env)->DeleteLocalRef(env, exception_class);
    241    (*env)->DeleteLocalRef(env, string);
    242 
    243    return ret;
    244 }
    245 
    246 int ff_jni_exception_check(JNIEnv *env, int log, void *log_ctx)
    247 {
    248    int ret;
    249 
    250    jthrowable exception;
    251 
    252    char *message = NULL;
    253 
    254    if (!(*(env))->ExceptionCheck((env))) {
    255        return 0;
    256    }
    257 
    258    if (!log) {
    259        (*(env))->ExceptionClear((env));
    260        return -1;
    261    }
    262 
    263    exception = (*env)->ExceptionOccurred(env);
    264    (*(env))->ExceptionClear((env));
    265 
    266    if ((ret = ff_jni_exception_get_summary(env, exception, &message, log_ctx)) < 0) {
    267        (*env)->DeleteLocalRef(env, exception);
    268        return ret;
    269    }
    270 
    271    (*env)->DeleteLocalRef(env, exception);
    272 
    273    av_log(log_ctx, AV_LOG_ERROR, "%s\n", message);
    274    av_free(message);
    275 
    276    return -1;
    277 }
    278 
    279 int ff_jni_init_jfields(JNIEnv *env, void *jfields, const struct FFJniField *jfields_mapping, int global, void *log_ctx)
    280 {
    281    int i, ret = 0;
    282    jclass last_clazz = NULL;
    283 
    284    for (i = 0; jfields_mapping[i].name; i++) {
    285        int mandatory = jfields_mapping[i].mandatory;
    286        enum FFJniFieldType type = jfields_mapping[i].type;
    287 
    288        if (type == FF_JNI_CLASS) {
    289            jclass clazz;
    290 
    291            last_clazz = NULL;
    292 
    293            clazz = (*env)->FindClass(env, jfields_mapping[i].name);
    294            if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
    295                goto done;
    296            }
    297 
    298            last_clazz = *(jclass*)((uint8_t*)jfields + jfields_mapping[i].offset) =
    299                    global ? (*env)->NewGlobalRef(env, clazz) : clazz;
    300 
    301            if (global) {
    302                (*env)->DeleteLocalRef(env, clazz);
    303            }
    304 
    305        } else {
    306 
    307            if (!last_clazz) {
    308                ret = AVERROR_EXTERNAL;
    309                break;
    310            }
    311 
    312            switch(type) {
    313            case FF_JNI_FIELD: {
    314                jfieldID field_id = (*env)->GetFieldID(env, last_clazz, jfields_mapping[i].method, jfields_mapping[i].signature);
    315                if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
    316                    goto done;
    317                }
    318 
    319                *(jfieldID*)((uint8_t*)jfields + jfields_mapping[i].offset) = field_id;
    320                break;
    321            }
    322            case FF_JNI_STATIC_FIELD: {
    323                jfieldID field_id = (*env)->GetStaticFieldID(env, last_clazz, jfields_mapping[i].method, jfields_mapping[i].signature);
    324                if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
    325                    goto done;
    326                }
    327 
    328                *(jfieldID*)((uint8_t*)jfields + jfields_mapping[i].offset) = field_id;
    329                break;
    330            }
    331            case FF_JNI_METHOD: {
    332                jmethodID method_id = (*env)->GetMethodID(env, last_clazz, jfields_mapping[i].method, jfields_mapping[i].signature);
    333                if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
    334                    goto done;
    335                }
    336 
    337                *(jmethodID*)((uint8_t*)jfields + jfields_mapping[i].offset) = method_id;
    338                break;
    339            }
    340            case FF_JNI_STATIC_METHOD: {
    341                jmethodID method_id = (*env)->GetStaticMethodID(env, last_clazz, jfields_mapping[i].method, jfields_mapping[i].signature);
    342                if ((ret = ff_jni_exception_check(env, mandatory, log_ctx)) < 0 && mandatory) {
    343                    goto done;
    344                }
    345 
    346                *(jmethodID*)((uint8_t*)jfields + jfields_mapping[i].offset) = method_id;
    347                break;
    348            }
    349            default:
    350                av_log(log_ctx, AV_LOG_ERROR, "Unknown JNI field type\n");
    351                ret = AVERROR(EINVAL);
    352                goto done;
    353            }
    354 
    355            ret = 0;
    356        }
    357    }
    358 
    359 done:
    360    if (ret < 0) {
    361        /* reset jfields in case of failure so it does not leak references */
    362        ff_jni_reset_jfields(env, jfields, jfields_mapping, global, log_ctx);
    363    }
    364 
    365    return ret;
    366 }
    367 
    368 int ff_jni_reset_jfields(JNIEnv *env, void *jfields, const struct FFJniField *jfields_mapping, int global, void *log_ctx)
    369 {
    370    int i;
    371 
    372    for (i = 0; jfields_mapping[i].name; i++) {
    373        enum FFJniFieldType type = jfields_mapping[i].type;
    374 
    375        switch(type) {
    376        case FF_JNI_CLASS: {
    377            jclass clazz = *(jclass*)((uint8_t*)jfields + jfields_mapping[i].offset);
    378            if (!clazz)
    379                continue;
    380 
    381            if (global) {
    382                (*env)->DeleteGlobalRef(env, clazz);
    383            } else {
    384                (*env)->DeleteLocalRef(env, clazz);
    385            }
    386 
    387            *(jclass*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
    388            break;
    389        }
    390        case FF_JNI_FIELD: {
    391            *(jfieldID*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
    392            break;
    393        }
    394        case FF_JNI_STATIC_FIELD: {
    395            *(jfieldID*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
    396            break;
    397        }
    398        case FF_JNI_METHOD: {
    399            *(jmethodID*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
    400            break;
    401        }
    402        case FF_JNI_STATIC_METHOD: {
    403            *(jmethodID*)((uint8_t*)jfields + jfields_mapping[i].offset) = NULL;
    404            break;
    405        }
    406        default:
    407            av_log(log_ctx, AV_LOG_ERROR, "Unknown JNI field type\n");
    408        }
    409    }
    410 
    411    return 0;
    412 }