bdj.c 33.6 KB
Newer Older
1 2 3
/*
 * This file is part of libbluray
 * Copyright (C) 2010  William Hahne
4
 * Copyright (C) 2012-2019  Petri Hintukainen <phintuka@users.sourceforge.net>
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.s
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see
 * <http://www.gnu.org/licenses/>.
 */

21 22 23
#if HAVE_CONFIG_H
#include "config.h"
#endif
24 25 26

#include "bdj.h"

27 28
#include "native/register_native.h"

29
#include "file/file.h"
30
#include "file/dirs.h"
31 32
#include "file/dl.h"
#include "util/strutl.h"
33
#include "util/macro.h"
34
#include "util/logging.h"
35

36 37

#include <jni.h>
38
#include <stdio.h>
39 40 41
#include <stdlib.h>
#include <string.h>

42 43 44 45 46 47 48
#ifdef __APPLE__
#include <sys/types.h>
#include <sys/wait.h>
#include <limits.h>
#include <unistd.h>
#endif

49 50 51 52 53
#ifdef _WIN32
#include <windows.h>
#include <winreg.h>
#endif

54 55 56 57 58 59
#ifdef HAVE_BDJ_J2ME
#define BDJ_JARFILE "libbluray-j2me-" VERSION ".jar"
#else
#define BDJ_JARFILE "libbluray-j2se-" VERSION ".jar"
#endif

60
struct bdjava_s {
Marvin Scholz's avatar
Marvin Scholz committed
61 62 63
#if defined(__APPLE__) && !defined(HAVE_BDJ_J2ME)
    void   *h_libjli;
#endif
64 65 66
    void   *h_libjvm;
    JavaVM *jvm;
};
67

68
typedef jint (JNICALL * fptr_JNI_CreateJavaVM) (JavaVM **pvm, void **penv,void *args);
69
typedef jint (JNICALL * fptr_JNI_GetCreatedJavaVMs) (JavaVM **vmBuf, jsize bufLen, jsize *nVMs);
70

71 72 73 74 75 76

#if defined(_WIN32) && !defined(HAVE_BDJ_J2ME)
static void *_load_dll(const wchar_t *lib_path, const wchar_t *dll_search_path)
{
    void *result;

77 78 79 80 81 82
    typedef PVOID(WINAPI *AddDllDirectoryF)  (PCWSTR);
    typedef BOOL(WINAPI *RemoveDllDirectoryF)(PVOID);
    AddDllDirectoryF pAddDllDirectory;
    RemoveDllDirectoryF pRemoveDllDirectory;
    pAddDllDirectory = (AddDllDirectoryF)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "AddDllDirectory");
    pRemoveDllDirectory = (RemoveDllDirectoryF)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "RemoveDllDirectory");
83 84

    if (pAddDllDirectory && pRemoveDllDirectory) {
85 86 87 88 89

        result = LoadLibraryExW(lib_path, NULL,
                               LOAD_LIBRARY_SEARCH_SYSTEM32);

        if (!result) {
Petri Hintukainen's avatar
Petri Hintukainen committed
90 91 92 93 94
            PVOID cookie = pAddDllDirectory(dll_search_path);
            result = LoadLibraryExW(lib_path, NULL,
                                    LOAD_LIBRARY_SEARCH_SYSTEM32 |
                                    LOAD_LIBRARY_SEARCH_USER_DIRS);
            pRemoveDllDirectory(cookie);
95
        }
96
    } else {
97 98
        result = LoadLibraryW(lib_path);
        if (!result) {
Petri Hintukainen's avatar
Petri Hintukainen committed
99 100 101
            SetDllDirectoryW(dll_search_path);
            result = LoadLibraryW(lib_path);
            SetDllDirectoryW(L"");
102
        }
103 104 105 106 107 108
    }

    return result;
}
#endif

109 110 111
#if defined(_WIN32) && !defined(HAVE_BDJ_J2ME)
static void *_load_jvm_win32(const char **p_java_home)
{
112
    static char java_home[256] = "";
113 114 115

    wchar_t buf_loc[4096] = L"SOFTWARE\\JavaSoft\\Java Runtime Environment\\";
    wchar_t buf_vers[128];
116
    wchar_t java_path[4096] = L"";
117 118 119 120 121 122 123 124
    char strbuf[256];

    LONG r;
    DWORD lType;
    DWORD dSize = sizeof(buf_vers);
    HKEY hkey;

    r = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buf_loc, 0, KEY_READ, &hkey);
125
# ifndef NO_JAVA9_SUPPORT
126 127 128 129 130
    if (r != ERROR_SUCCESS) {
        /* Try Java 9 */
        wcscpy(buf_loc, L"SOFTWARE\\JavaSoft\\JRE\\");
        r = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buf_loc, 0, KEY_READ, &hkey);
    }
131
# endif
132
    if (r != ERROR_SUCCESS) {
133
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "Error opening registry key SOFTWARE\\JavaSoft\\Java Runtime Environment\\\n");
134 135 136 137 138 139
        return NULL;
    }

    r = RegQueryValueExW(hkey, L"CurrentVersion", NULL, &lType, (LPBYTE)buf_vers, &dSize);
    RegCloseKey(hkey);
    if (r != ERROR_SUCCESS) {
140
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "CurrentVersion registry value not found\n");
141 142 143 144
        return NULL;
    }

    if (debug_mask & DBG_BDJ) {
145 146 147
        if (!WideCharToMultiByte(CP_UTF8, 0, buf_vers, -1, strbuf, sizeof(strbuf), NULL, NULL)) {
            strbuf[0] = 0;
        }
148 149 150 151 152 153 154
        BD_DEBUG(DBG_BDJ, "JRE version: %s\n", strbuf);
    }
    wcscat(buf_loc, buf_vers);

    dSize = sizeof(buf_loc);
    r = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buf_loc, 0, KEY_READ, &hkey);
    if (r != ERROR_SUCCESS) {
155
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "Error opening JRE version-specific registry key\n");
156 157 158 159 160 161 162
        return NULL;
    }

    r = RegQueryValueExW(hkey, L"JavaHome", NULL, &lType, (LPBYTE)buf_loc, &dSize);

    if (r == ERROR_SUCCESS) {
        /* do not fail even if not found */
163 164 165
        if (WideCharToMultiByte(CP_UTF8, 0, buf_loc, -1, java_home, sizeof(java_home), NULL, NULL)) {
            *p_java_home = java_home;
        }
166
        BD_DEBUG(DBG_BDJ, "JavaHome: %s\n", java_home);
167 168 169

        wcscat(java_path, buf_loc);
        wcscat(java_path, L"\\bin");
170 171 172 173 174 175 176
    }

    dSize = sizeof(buf_loc);
    r = RegQueryValueExW(hkey, L"RuntimeLib", NULL, &lType, (LPBYTE)buf_loc, &dSize);
    RegCloseKey(hkey);

    if (r != ERROR_SUCCESS) {
177
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "RuntimeLib registry value not found\n");
178 179 180
        return NULL;
    }

181 182

    void *result = _load_dll(buf_loc, java_path);
183

184 185 186
    if (!WideCharToMultiByte(CP_UTF8, 0, buf_loc, -1, strbuf, sizeof(strbuf), NULL, NULL)) {
        strbuf[0] = 0;
    }
187 188 189 190 191 192 193 194 195 196
    if (!result) {
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "can't open library '%s'\n", strbuf);
    } else {
        BD_DEBUG(DBG_BDJ, "Using JRE library %s\n", strbuf);
    }

    return result;
}
#endif

197 198 199 200
#ifdef _WIN32
static inline char *_utf8_to_cp(const char *utf8)
{
    int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
201
    if (wlen <= 0) {
202 203 204 205 206 207 208
        return NULL;
    }

    wchar_t *wide = (wchar_t *)malloc(wlen * sizeof(wchar_t));
    if (!wide) {
        return NULL;
    }
209 210 211 212
    if (!MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wide, wlen)) {
        X_FREE(wide);
        return NULL;
    }
213 214

    size_t len = WideCharToMultiByte(CP_ACP, 0, wide, -1, NULL, 0, NULL, NULL);
215
    if (len <= 0) {
216 217 218 219 220 221
        X_FREE(wide);
        return NULL;
    }

    char *out = (char *)malloc(len);
    if (out != NULL) {
222 223 224
        if (!WideCharToMultiByte(CP_ACP, 0, wide, -1, out, len, NULL, NULL)) {
            X_FREE(out);
        }
225 226 227 228 229 230
    }
    X_FREE(wide);
    return out;
}
#endif

231 232 233 234 235 236
#ifdef __APPLE__
// The current official JRE is installed by Oracle's Java Applet internet plugin:
#define MACOS_JRE_HOME "/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home"
static const char *jre_plugin_path = MACOS_JRE_HOME;
#endif

237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
#if defined(__APPLE__) && !defined(HAVE_BDJ_J2ME)

#define MACOS_JAVA_HOME "/usr/libexec/java_home"
static char *_java_home_macos()
{
    static char result[PATH_MAX] = "";

    if (result[0])
        return result;

    pid_t java_home_pid;
    int fd[2], exitcode;

    if (pipe(fd)) {
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "unable to set up pipes\n");
        return NULL;
    }

    switch (java_home_pid = vfork())
    {
        case -1:
            BD_DEBUG(DBG_BDJ | DBG_CRIT, "vfork failed\n");
            return NULL;

        case 0:
            if (dup2(fd[1], STDOUT_FILENO) == -1) {
                _exit(-1);
            }

            close(fd[1]);
            close(fd[0]);

            execl(MACOS_JAVA_HOME, MACOS_JAVA_HOME);

            _exit(-1);

        default:
            close(fd[1]);

            for (int len = 0; ;) {
                int n = read(fd[0], result + len, sizeof result - len);
                if (n <= 0)
                    break;

                len += n;
                result[len-1] = '\0';
            }

            waitpid(java_home_pid, &exitcode, 0);
    }

    if (result[0] == '\0' || exitcode) {
        BD_DEBUG(DBG_BDJ | DBG_CRIT,
                 "Unable to read path from " MACOS_JAVA_HOME "\n");
        result[0] = '\0';
        return NULL;
    }

    BD_DEBUG(DBG_BDJ, "macos java home: '%s'\n", result );
    return result;
}
#undef MACOS_JAVA_HOME

#endif

Petri Hintukainen's avatar
Petri Hintukainen committed
302
static void *_jvm_dlopen(const char *java_home, const char *jvm_dir, const char *jvm_lib)
Petri Hintukainen's avatar
Petri Hintukainen committed
303
{
Petri Hintukainen's avatar
Petri Hintukainen committed
304
    if (java_home) {
305
        char *path = str_printf("%s" DIR_SEP "%s" DIR_SEP "%s", java_home, jvm_dir, jvm_lib);
306 307 308 309
        if (!path) {
            BD_DEBUG(DBG_CRIT, "out of memory\n");
            return NULL;
        }
Petri Hintukainen's avatar
Petri Hintukainen committed
310
        BD_DEBUG(DBG_BDJ, "Opening %s ...\n", path);
Petri Hintukainen's avatar
Petri Hintukainen committed
311
        void *h = dl_dlopen(path, NULL);
312 313
# ifdef NO_JAVA9_SUPPORT
        /* ignore Java 9+ */
314 315 316 317 318
        if (h && dl_dlsym(h, "JVM_DefineModule")) {
            BD_DEBUG(DBG_CRIT | DBG_BDJ, "Ignoring JVM %s: looks like Java 9 or later\n", path);
            dl_dlclose(h);
            h = NULL;
        }
319
# endif
Petri Hintukainen's avatar
Petri Hintukainen committed
320 321 322
        X_FREE(path);
        return h;
    } else {
Petri Hintukainen's avatar
Petri Hintukainen committed
323
        BD_DEBUG(DBG_BDJ, "Opening %s ...\n", jvm_lib);
Petri Hintukainen's avatar
Petri Hintukainen committed
324 325 326
        return dl_dlopen(jvm_lib, NULL);
    }
}
327

328 329 330 331 332 333 334 335 336 337 338 339 340 341
static void *_jvm_dlopen_a(const char *java_home,
                           const char * const *jvm_dir, unsigned num_jvm_dir,
                           const char *jvm_lib)
{
  unsigned ii;
  void *dll = NULL;

  for (ii = 0; !dll && ii < num_jvm_dir; ii++) {
      dll = _jvm_dlopen(java_home, jvm_dir[ii], jvm_lib);
  }

  return dll;
}

Marvin Scholz's avatar
Marvin Scholz committed
342 343 344 345 346 347
#if defined(__APPLE__) && !defined(HAVE_BDJ_J2ME)
static void *_load_jli_macos()
{
    const char *java_home = NULL;
    static const char jli_dir[] = "jre/lib/jli";
    static const char jli_lib[] = "libjli";
348
    void *handle;
Marvin Scholz's avatar
Marvin Scholz committed
349 350 351 352 353 354 355 356 357

    /* JAVA_HOME set, use it */
    java_home = getenv("JAVA_HOME");
    if (java_home) {
        return _jvm_dlopen(java_home, jli_dir, jli_lib);
    }

    java_home = _java_home_macos();
    if (java_home) {
358 359 360 361
        handle = _jvm_dlopen(java_home, jli_dir, jli_lib);
        if (handle) {
            return handle;
        }
Marvin Scholz's avatar
Marvin Scholz committed
362
    }
363 364
    // check if the JRE is installed:
    return _jvm_dlopen(jre_plugin_path, "lib/jli", jli_lib);
Marvin Scholz's avatar
Marvin Scholz committed
365 366 367
}
#endif

Petri Hintukainen's avatar
Petri Hintukainen committed
368 369 370
static void *_load_jvm(const char **p_java_home)
{
#ifdef HAVE_BDJ_J2ME
Andreas Zelend's avatar
Andreas Zelend committed
371
# ifdef _WIN32
Petri Hintukainen's avatar
Petri Hintukainen committed
372
    static const char * const jvm_path[] = {NULL, JDK_HOME};
373
    static const char * const jvm_dir[]  = {"bin"};
Petri Hintukainen's avatar
Petri Hintukainen committed
374
    static const char         jvm_lib[]  = "cvmi";
Petri Hintukainen's avatar
Petri Hintukainen committed
375
# else
Petri Hintukainen's avatar
Petri Hintukainen committed
376
    static const char * const jvm_path[] = {NULL, JDK_HOME, "/opt/PhoneME"};
377
    static const char * const jvm_dir[]  = {"bin"};
Petri Hintukainen's avatar
Petri Hintukainen committed
378
    static const char         jvm_lib[]  = "libcvm";
Petri Hintukainen's avatar
Petri Hintukainen committed
379
# endif
380
#else /* HAVE_BDJ_J2ME */
Andreas Zelend's avatar
Andreas Zelend committed
381
# ifdef _WIN32
Petri Hintukainen's avatar
Petri Hintukainen committed
382
    static const char * const jvm_path[] = {NULL, JDK_HOME};
383 384 385 386 387
    static const char * const jvm_dir[]  = {"jre\\bin\\server",
                                            "bin\\server",
                                            "jre\\bin\\client",
                                            "bin\\client",
    };
Petri Hintukainen's avatar
Petri Hintukainen committed
388
    static const char         jvm_lib[]  = "jvm";
Petri Hintukainen's avatar
Petri Hintukainen committed
389
# else
390
#  ifdef __APPLE__
391 392 393
    static const char * const jvm_path[] = {NULL, JDK_HOME, MACOS_JRE_HOME};
    static const char * const jvm_dir[]  = {"jre/lib/server",
                                            "lib/server"};
394
#  else
Petri Hintukainen's avatar
Petri Hintukainen committed
395 396 397 398 399 400 401
    static const char * const jvm_path[] = {NULL,
                                            JDK_HOME,
                                            "/usr/lib/jvm/default-java",
                                            "/usr/lib/jvm/default",
                                            "/usr/lib/jvm/",
                                            "/etc/java-config-2/current-system-vm",
                                            "/usr/lib/jvm/java-7-openjdk",
402
                                            "/usr/lib/jvm/java-7-openjdk-" JAVA_ARCH,
Petri Hintukainen's avatar
Petri Hintukainen committed
403
                                            "/usr/lib/jvm/java-8-openjdk",
404
                                            "/usr/lib/jvm/java-8-openjdk-" JAVA_ARCH,
Petri Hintukainen's avatar
Petri Hintukainen committed
405
                                            "/usr/lib/jvm/java-6-openjdk",
Petri Hintukainen's avatar
Petri Hintukainen committed
406
    };
407
    static const char * const jvm_dir[]  = {"jre/lib/" JAVA_ARCH "/server",
408 409 410
                                            "lib/server",
                                            "lib/client",
    };
411
#  endif
Petri Hintukainen's avatar
Petri Hintukainen committed
412
    static const char         jvm_lib[]  = "libjvm";
Petri Hintukainen's avatar
Petri Hintukainen committed
413 414
# endif
#endif
415 416 417
    const unsigned num_jvm_dir  = sizeof(jvm_dir)  / sizeof(jvm_dir[0]);
    const unsigned num_jvm_path = sizeof(jvm_path) / sizeof(jvm_path[0]);

Petri Hintukainen's avatar
Petri Hintukainen committed
418 419 420 421 422 423 424 425
    const char *java_home = NULL;
    unsigned    path_ind;
    void       *handle = NULL;

    /* JAVA_HOME set, use it */
    java_home = getenv("JAVA_HOME");
    if (java_home) {
        *p_java_home = java_home;
426
        return _jvm_dlopen_a(java_home, jvm_dir, num_jvm_dir, jvm_lib);
Petri Hintukainen's avatar
Petri Hintukainen committed
427 428
    }

429 430 431 432 433 434 435
#if defined(_WIN32) && !defined(HAVE_BDJ_J2ME)
    handle = _load_jvm_win32(p_java_home);
    if (handle) {
        return handle;
    }
#endif

436 437 438
#if defined(__APPLE__) && !defined(HAVE_BDJ_J2ME)
    java_home = _java_home_macos();
    if (java_home) {
439 440 441 442 443 444 445 446 447 448 449
        handle = _jvm_dlopen_a(java_home, jvm_dir, num_jvm_dir, jvm_lib);
        if (handle) {
            *p_java_home = java_home;
            return handle;
        }
    }
    // check if the JRE is installed:
    handle = _jvm_dlopen(jre_plugin_path, "lib/server", jvm_lib);
    if (handle) {
        *p_java_home = jre_plugin_path;
        return handle;
450 451 452
    }
#endif

Petri Hintukainen's avatar
Petri Hintukainen committed
453
    BD_DEBUG(DBG_BDJ, "JAVA_HOME not set, trying default locations\n");
Petri Hintukainen's avatar
Petri Hintukainen committed
454 455

    /* try our pre-defined locations */
456
    for (path_ind = 0; !handle && path_ind < num_jvm_path; path_ind++) {
Petri Hintukainen's avatar
Petri Hintukainen committed
457
        *p_java_home = jvm_path[path_ind];
458
        handle = _jvm_dlopen_a(jvm_path[path_ind], jvm_dir, num_jvm_dir, jvm_lib);
Petri Hintukainen's avatar
Petri Hintukainen committed
459
    }
Petri Hintukainen's avatar
Petri Hintukainen committed
460

461 462 463 464
    if (!*p_java_home) {
        *p_java_home = dl_get_path();
    }

Petri Hintukainen's avatar
Petri Hintukainen committed
465
    return handle;
Petri Hintukainen's avatar
Petri Hintukainen committed
466
}
467

468 469
static int _can_read_file(const char *fn)
{
470
    BD_FILE_H *fp;
Petri Hintukainen's avatar
Petri Hintukainen committed
471 472 473 474 475

    if (!fn) {
        return 0;
    }

476
    fp = file_open(fn, "rb");
477
    if (fp) {
478
        uint8_t b;
479 480
        int result = (int)file_read(fp, &b, 1);
        file_close(fp);
481 482 483 484 485 486 487 488
        if (result == 1) {
            return 1;
        }
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "Error reading %s\n", fn);
    }
    return 0;
}

489 490 491 492
void bdj_storage_cleanup(BDJ_STORAGE *p)
{
    X_FREE(p->cache_root);
    X_FREE(p->persistent_root);
Petri Hintukainen's avatar
Petri Hintukainen committed
493 494
    X_FREE(p->classpath[0]);
    X_FREE(p->classpath[1]);
495 496
}

Petri Hintukainen's avatar
Petri Hintukainen committed
497
static char *_find_libbluray_jar0()
498 499 500
{
    // pre-defined search paths for libbluray.jar
    static const char * const jar_paths[] = {
Petri Hintukainen's avatar
Petri Hintukainen committed
501
#ifndef _WIN32
502
        "/usr/share/java/" BDJ_JARFILE,
503
        "/usr/share/libbluray/lib/" BDJ_JARFILE,
504
#endif
Petri Hintukainen's avatar
Petri Hintukainen committed
505
        BDJ_JARFILE,
506 507 508 509
    };

    unsigned i;

510
    // check if overriding the classpath
511
    const char *classpath = getenv("LIBBLURAY_CP");
512
    if (classpath) {
513
        size_t cp_len = strlen(classpath);
Petri Hintukainen's avatar
Petri Hintukainen committed
514
        char *jar;
515 516 517

        // directory or file ?
        if (cp_len > 0 && (classpath[cp_len - 1] == '/' || classpath[cp_len - 1] == '\\')) {
Petri Hintukainen's avatar
Petri Hintukainen committed
518
            jar = str_printf("%s%s", classpath, BDJ_JARFILE);
519
        } else {
Petri Hintukainen's avatar
Petri Hintukainen committed
520
            jar = str_dup(classpath);
521 522
        }

Petri Hintukainen's avatar
Petri Hintukainen committed
523
        if (!jar) {
Petri Hintukainen's avatar
Petri Hintukainen committed
524 525 526 527
            BD_DEBUG(DBG_CRIT, "out of memory\n");
            return NULL;
        }

Petri Hintukainen's avatar
Petri Hintukainen committed
528 529
        if (_can_read_file(jar)) {
            return jar;
530 531
        }

Petri Hintukainen's avatar
Petri Hintukainen committed
532
        X_FREE(jar);
533 534
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "invalid LIBBLURAY_CP %s\n", classpath);
        return NULL;
535 536 537 538
    }

    BD_DEBUG(DBG_BDJ, "LIBBLURAY_CP not set, searching for "BDJ_JARFILE" ...\n");

539 540 541 542
    // check directory where libbluray.so was loaded from
    const char *lib_path = dl_get_path();
    if (lib_path) {
        char *cp = str_printf("%s" BDJ_JARFILE, lib_path);
Petri Hintukainen's avatar
Petri Hintukainen committed
543 544 545 546 547
        if (!cp) {
            BD_DEBUG(DBG_CRIT, "out of memory\n");
            return NULL;
        }

548
        BD_DEBUG(DBG_BDJ, "Checking %s ...\n", cp);
549
        if (_can_read_file(cp)) {
550 551 552 553 554 555
            BD_DEBUG(DBG_BDJ, "using %s\n", cp);
            return cp;
        }
        X_FREE(cp);
    }

556 557 558
    // check pre-defined directories
    for (i = 0; i < sizeof(jar_paths) / sizeof(jar_paths[0]); i++) {
        BD_DEBUG(DBG_BDJ, "Checking %s ...\n", jar_paths[i]);
559
        if (_can_read_file(jar_paths[i])) {
Petri Hintukainen's avatar
Petri Hintukainen committed
560 561
            BD_DEBUG(DBG_BDJ, "using %s\n", jar_paths[i]);
            return str_dup(jar_paths[i]);
562 563 564 565
        }
    }

    BD_DEBUG(DBG_BDJ | DBG_CRIT, BDJ_JARFILE" not found.\n");
566
    return NULL;
567 568
}

Petri Hintukainen's avatar
Petri Hintukainen committed
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
static char *_find_libbluray_jar1(const char *jar0)
{
    char *jar1;
    int   cut;

    cut = (int)strlen(jar0) - (int)strlen(VERSION) - 9;
    if (cut <= 0)
        return NULL;

    jar1 = str_printf("%.*sawt-%s", cut, jar0, jar0 + cut);
    if (!jar1)
        return NULL;

    if (!_can_read_file(jar1)) {
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "Cant access AWT jar file %s\n", jar1);
        X_FREE(jar1);
    }

    return jar1;
}

static int _find_libbluray_jar(BDJ_STORAGE *storage)
{
    if (!storage->classpath[0]) {
        storage->classpath[0] = _find_libbluray_jar0();
        X_FREE(storage->classpath[1]);
        if (!storage->classpath[0])
            return 0;
    }

    if (!storage->classpath[1]) {
        storage->classpath[1] = _find_libbluray_jar1(storage->classpath[0]);
        if (!storage->classpath[1]) {
            X_FREE(storage->classpath[0]);
            X_FREE(storage->classpath[1]);
        }
    }

    return !!storage->classpath[0];
}

610
static const char *_bdj_persistent_root(BDJ_STORAGE *storage)
611
{
612
    const char *root;
613
    char       *data_home;
614

615 616 617 618
    if (storage->no_persistent_storage) {
        return NULL;
    }

Petri Hintukainen's avatar
Petri Hintukainen committed
619
    if (!storage->persistent_root) {
620

Petri Hintukainen's avatar
Petri Hintukainen committed
621 622 623 624
        root = getenv("LIBBLURAY_PERSISTENT_ROOT");
        if (root) {
            return root;
        }
625

626
        data_home = file_get_data_home();
627 628 629 630 631
        if (data_home) {
            storage->persistent_root = str_printf("%s" DIR_SEP "bluray" DIR_SEP "dvb.persistent.root" DIR_SEP, data_home);
            X_FREE(data_home);
            BD_DEBUG(DBG_BDJ, "LIBBLURAY_PERSISTENT_ROOT not set, using %s\n", storage->persistent_root);
        }
632

633 634 635
        if (!storage->persistent_root) {
            BD_DEBUG(DBG_BDJ | DBG_CRIT, "WARNING: BD-J persistent root not set\n");
        }
Petri Hintukainen's avatar
Petri Hintukainen committed
636
    }
637

638
    return storage->persistent_root;
639 640
}

641
static const char *_bdj_buda_root(BDJ_STORAGE *storage)
642
{
643
    const char *root;
644
    char       *cache_home;
645

646 647 648 649
    if (storage->no_persistent_storage) {
        return NULL;
    }

Petri Hintukainen's avatar
Petri Hintukainen committed
650
    if (!storage->cache_root) {
651

Petri Hintukainen's avatar
Petri Hintukainen committed
652 653 654 655
        root = getenv("LIBBLURAY_CACHE_ROOT");
        if (root) {
            return root;
        }
656

657
        cache_home = file_get_cache_home();
658 659 660 661 662
        if (cache_home) {
            storage->cache_root = str_printf("%s" DIR_SEP "bluray" DIR_SEP "bluray.bindingunit.root" DIR_SEP, cache_home);
            X_FREE(cache_home);
            BD_DEBUG(DBG_BDJ, "LIBBLURAY_CACHE_ROOT not set, using %s\n", storage->cache_root);
        }
663

664 665 666
        if (!storage->cache_root) {
            BD_DEBUG(DBG_BDJ | DBG_CRIT, "WARNING: BD-J cache root not set\n");
        }
Petri Hintukainen's avatar
Petri Hintukainen committed
667
    }
668

669
    return storage->cache_root;
670 671
}

672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
static int _get_method(JNIEnv *env, jclass *cls, jmethodID *method_id,
                       const char *class_name, const char *method_name, const char *method_sig)
{
    *method_id = NULL;
    *cls = (*env)->FindClass(env, class_name);
    if (!*cls) {
        (*env)->ExceptionDescribe(env);
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "Failed to locate class %s\n", class_name);
        (*env)->ExceptionClear(env);
        return 0;
    }

    *method_id = (*env)->GetStaticMethodID(env, *cls, method_name, method_sig);
    if (!*method_id) {
        (*env)->ExceptionDescribe(env);
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "Failed to locate class %s method %s %s\n",
                 class_name, method_name, method_sig);
        (*env)->DeleteLocalRef(env, *cls);
        *cls = NULL;
        (*env)->ExceptionClear(env);
        return 0;
    }

    return 1;
}

698 699
static int _bdj_init(JNIEnv *env, struct bluray *bd, const char *disc_root, const char *bdj_disc_id,
                     BDJ_STORAGE *storage)
Petri Hintukainen's avatar
Petri Hintukainen committed
700
{
701 702 703 704
    if (!bdj_register_native_methods(env)) {
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "Couldn't register native methods.\n");
    }

Petri Hintukainen's avatar
Petri Hintukainen committed
705
    // initialize class org.videolan.Libbluray
706 707
    jclass init_class;
    jmethodID init_id;
708
    if (!_get_method(env, &init_class, &init_id,
709 710
                     "org/videolan/Libbluray", "init",
                     "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V")) {
Petri Hintukainen's avatar
Petri Hintukainen committed
711 712 713
        return 0;
    }

714
    const char *disc_id = (bdj_disc_id && bdj_disc_id[0]) ? bdj_disc_id : "00000000000000000000000000000000";
715
    jlong param_bdjava_ptr = (jlong)(intptr_t) bd;
716
    jstring param_disc_id = (*env)->NewStringUTF(env, disc_id);
717
    jstring param_disc_root = (*env)->NewStringUTF(env, disc_root);
718 719 720
    jstring param_persistent_root = (*env)->NewStringUTF(env, _bdj_persistent_root(storage));
    jstring param_buda_root = (*env)->NewStringUTF(env, _bdj_buda_root(storage));

Petri Hintukainen's avatar
Petri Hintukainen committed
721
    (*env)->CallStaticVoidMethod(env, init_class, init_id,
722 723
                                 param_bdjava_ptr, param_disc_id, param_disc_root,
                                 param_persistent_root, param_buda_root);
724

725 726 727 728 729 730
    (*env)->DeleteLocalRef(env, init_class);
    (*env)->DeleteLocalRef(env, param_disc_id);
    (*env)->DeleteLocalRef(env, param_disc_root);
    (*env)->DeleteLocalRef(env, param_persistent_root);
    (*env)->DeleteLocalRef(env, param_buda_root);

731 732
    if ((*env)->ExceptionOccurred(env)) {
        (*env)->ExceptionDescribe(env);
733
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "Failed to initialize BD-J (uncaught exception)\n");
734
        (*env)->ExceptionClear(env);
735
        return 0;
736 737
    }

Petri Hintukainen's avatar
Petri Hintukainen committed
738 739 740
    return 1;
}

741
int bdj_jvm_available(BDJ_STORAGE *storage)
742 743 744 745 746
{
    const char *java_home;
    void* jvm_lib = _load_jvm(&java_home);
    if (!jvm_lib) {
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "BD-J check: Failed to load JVM library\n");
747
        return BDJ_CHECK_NO_JVM;
748 749 750
    }
    dl_dlclose(jvm_lib);

751 752
    if (!_find_libbluray_jar(storage)) {
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "BD-J check: Failed to load libbluray.jar\n");
753
        return BDJ_CHECK_NO_JAR;
754 755 756 757
    }

    BD_DEBUG(DBG_BDJ, "BD-J check: OK\n");

758
    return BDJ_CHECK_OK;
759 760
}

761 762
static int _find_jvm(void *jvm_lib, JNIEnv **env, JavaVM **jvm)
{
Petri Hintukainen's avatar
Petri Hintukainen committed
763 764
    fptr_JNI_GetCreatedJavaVMs JNI_GetCreatedJavaVMs_fp;

Petri Hintukainen's avatar
Petri Hintukainen committed
765
    *(void **)(&JNI_GetCreatedJavaVMs_fp) = dl_dlsym(jvm_lib, "JNI_GetCreatedJavaVMs");
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
    if (JNI_GetCreatedJavaVMs_fp == NULL) {
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "Couldn't find symbol JNI_GetCreatedJavaVMs.\n");
        return 0;
    }

    jsize nVMs = 0;
    JavaVM* javavm = NULL;

    int result = JNI_GetCreatedJavaVMs_fp(&javavm, 1, &nVMs);
    if (result == JNI_OK && nVMs > 0) {
      *jvm = javavm;
      (**jvm)->AttachCurrentThread(*jvm, (void**)env, NULL);
      return 1;
    }

    return 0;
}

784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839
/* Export packages for Xlets */
static const char * const java_base_exports[] = {
        "javax.media" ,
        "javax.media.protocol",
        "javax.tv.graphics",
        "javax.tv.service",
        "javax.tv.service.guide",
        "javax.tv.service.selection",
        "javax.tv.service.transport",
        "javax.tv.service.navigation",
        "javax.tv.net",
        "javax.tv.locator",
        "javax.tv.util",
        "javax.tv.media",
        "javax.tv.xlet",
        "javax.microedition.xlet",
        "org.davic.resources",
        "org.davic.net",
        "org.davic.media",
        "org.davic.mpeg",
        "org.dvb.user",
        "org.dvb.dsmcc",
        "org.dvb.application",
        "org.dvb.ui",
        "org.dvb.test",
        "org.dvb.lang",
        "org.dvb.event",
        "org.dvb.io.ixc",
        "org.dvb.io.persistent",
        "org.dvb.media",
        "org.havi.ui",
        "org.havi.ui.event",
        "org.bluray.application",
        "org.bluray.ui",
        "org.bluray.ui.event",
        "org.bluray.net",
        "org.bluray.storage",
        "org.bluray.vfs",
        "org.bluray.bdplus",
        "org.bluray.system",
        "org.bluray.media",
        "org.bluray.ti",
        "org.bluray.ti.selection",
        "org.blurayx.s3d.ui",
        "org.blurayx.s3d.system",
        "org.blurayx.s3d.media",
        "org.blurayx.s3d.ti",
        "org.blurayx.uhd.ui",
        "org.blurayx.uhd.system",
        "org.blurayx.uhd.ti",
        "com.aacsla.bluray.online",
        "com.aacsla.bluray.mc",
        "com.aacsla.bluray.mt",
};
static const size_t num_java_base_exports = sizeof(java_base_exports) / sizeof(java_base_exports[0]);

Petri Hintukainen's avatar
Petri Hintukainen committed
840
static int _create_jvm(void *jvm_lib, const char *java_home, const char *jar_file[2],
841
                       JNIEnv **env, JavaVM **jvm)
842
{
843
    (void)java_home;  /* used only with J2ME */
844

845
    fptr_JNI_CreateJavaVM JNI_CreateJavaVM_fp;
846
    JavaVMOption option[96];
847
    int n = 0, result, java_9;
Petri Hintukainen's avatar
Petri Hintukainen committed
848
    JavaVMInitArgs args;
849

Petri Hintukainen's avatar
Petri Hintukainen committed
850
    *(void **)(&JNI_CreateJavaVM_fp) = dl_dlsym(jvm_lib, "JNI_CreateJavaVM");
Petri Hintukainen's avatar
Petri Hintukainen committed
851 852
    if (JNI_CreateJavaVM_fp == NULL) {
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "Couldn't find symbol JNI_CreateJavaVM.\n");
853
        return 0;
Petri Hintukainen's avatar
Petri Hintukainen committed
854 855
    }

856 857 858 859 860 861 862 863 864
#ifdef HAVE_BDJ_J2ME
    java_9 = 0;
#else
    java_9 = !!dl_dlsym(jvm_lib, "JVM_DefineModule");
    if (java_9) {
        BD_DEBUG(DBG_CRIT | DBG_BDJ, "Detected Java 9 or later JVM - support is experimental !\n");
    }
#endif

Petri Hintukainen's avatar
Petri Hintukainen committed
865
    memset(option, 0, sizeof(option));
Petri Hintukainen's avatar
Petri Hintukainen committed
866

Petri Hintukainen's avatar
Petri Hintukainen committed
867
    option[n++].optionString = str_dup   ("-Dawt.toolkit=java.awt.BDToolkit");
868
    option[n++].optionString = str_dup   ("-Djava.awt.graphicsenv=java.awt.BDGraphicsEnvironment");
Petri Hintukainen's avatar
Petri Hintukainen committed
869 870 871 872 873 874
    option[n++].optionString = str_dup   ("-Xms256M");
    option[n++].optionString = str_dup   ("-Xmx256M");
    option[n++].optionString = str_dup   ("-Xss2048k");
#ifdef HAVE_BDJ_J2ME
    option[n++].optionString = str_printf("-Djava.home=%s", java_home);
    option[n++].optionString = str_printf("-Xbootclasspath/a:%s/lib/xmlparser.jar", java_home);
875
    option[n++].optionString = str_dup   ("-XfullShutdown");
876
#endif
877

878 879
    if (!java_9) {
      option[n++].optionString = str_dup   ("-Djavax.accessibility.assistive_technologies= ");
Petri Hintukainen's avatar
Petri Hintukainen committed
880
      option[n++].optionString = str_printf("-Xbootclasspath/p:%s:%s", jar_file[0], jar_file[1]);
881
    } else {
Petri Hintukainen's avatar
Petri Hintukainen committed
882 883
      option[n++].optionString = str_printf("--patch-module=java.base=%s", jar_file[0]);
      option[n++].optionString = str_printf("--patch-module=java.desktop=%s", jar_file[1]);
884 885 886

      /* Fix module graph */

887 888 889 890 891 892 893 894 895 896 897 898
      option[n++].optionString = str_dup("--add-reads=java.base=java.desktop");
      /* org.videolan.IxcRegistryImpl -> java.rmi.Remote */
      option[n++].optionString = str_dup("--add-reads=java.base=java.rmi");
      /* org.videolan.FontIndex -> java.xml. */
      option[n++].optionString = str_dup("--add-reads=java.base=java.xml");
      /* AWT needs to access logger and Xlet context */
      option[n++].optionString = str_dup("--add-opens=java.base/org.videolan=java.desktop");
      /* AWT needs to acess DVBGraphics */
      option[n++].optionString = str_dup("--add-exports=java.base/org.dvb.ui=java.desktop");
      /* org.havi.ui.HBackgroundImage needs to access sun.awt.image.FileImageSource */
      option[n++].optionString = str_dup("--add-exports=java.desktop/sun.awt.image=java.base");

899 900 901 902
      /* Export BluRay packages to Xlets */
      for (size_t idx = 0; idx < num_java_base_exports; idx++) {
          option[n++].optionString = str_printf("--add-exports=java.base/%s=ALL-UNNAMED", java_base_exports[idx]);
      }
903 904
    }

905
    /* JVM debug options */
906

907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925
    if (getenv("BDJ_JVM_DEBUG")) {
        option[n++].optionString = str_dup("-ea");
        //option[n++].optionString = str_dup("-verbose");
        //option[n++].optionString = str_dup("-verbose:class,gc,jni");
        option[n++].optionString = str_dup("-Xdebug");
        option[n++].optionString = str_dup("-Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n");
    }

#ifdef HAVE_BDJ_J2ME
    /*
      see: http://docs.oracle.com/javame/config/cdc/cdc-opt-impl/ojmeec/1.0/runtime/html/cvm.htm#CACBHBJB
      trace method execution: BDJ_JVM_TRACE=0x0002
      trace exceptions:       BDJ_JVM_TRACE=0x4000
    */
    if (getenv("BDJ_JVM_TRACE")) {
        option[n++].optionString = str_printf("-Xtrace:%s", getenv("BDJ_JVM_TRACE"));
    }
#endif

926
    args.version = JNI_VERSION_1_4;
927
    args.nOptions = n;
928 929
    args.options = option;
    args.ignoreUnrecognized = JNI_FALSE; // don't ignore unrecognized options
930

931 932 933 934 935 936
#ifdef _WIN32
    /* ... in windows, JVM options are not UTF8 but current system code page ... */
    /* luckily, most BD-J options can be passed in as java strings later. But, not class path. */
    int ii;
    for (ii = 0; ii < n; ii++) {
        char *tmp = _utf8_to_cp(option[ii].optionString);
937 938 939 940 941 942
        if (tmp) {
            X_FREE(option[ii].optionString);
            option[ii].optionString = tmp;
        } else {
            BD_DEBUG(DBG_BDJ | DBG_CRIT, "Failed to convert %s\n", option[ii].optionString);
        }
943 944 945
    }
#endif

Petri Hintukainen's avatar
Petri Hintukainen committed
946
    result = JNI_CreateJavaVM_fp(jvm, (void**) env, &args);
Petri Hintukainen's avatar
Petri Hintukainen committed
947

948 949 950
    while (--n >= 0) {
        X_FREE(option[n].optionString);
    }
951

952
    if (result != JNI_OK || !*env) {
953
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "Failed to create new Java VM. JNI_CreateJavaVM result: %d\n", result);
954 955 956 957 958 959 960
        return 0;
    }

    return 1;
}

BDJAVA* bdj_open(const char *path, struct bluray *bd,
961
                 const char *bdj_disc_id, BDJ_STORAGE *storage)
962 963 964
{
    BD_DEBUG(DBG_BDJ, "bdj_open()\n");

Petri Hintukainen's avatar
Petri Hintukainen committed
965
    if (!_find_libbluray_jar(storage)) {
966 967 968
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "BD-J start failed: " BDJ_JARFILE " not found.\n");
        return NULL;
    }
969

Marvin Scholz's avatar
Marvin Scholz committed
970 971 972 973 974 975 976 977 978 979
#if defined(__APPLE__) && !defined(HAVE_BDJ_J2ME)
    /* On macOS we need to load libjli to workaround a bug where the wrong
     * version would be used: https://bugs.openjdk.java.net/browse/JDK-7131356
     */
    void* jli_lib = _load_jli_macos();
    if (!jli_lib) {
        BD_DEBUG(DBG_BDJ, "Wasn't able to load JLI\n");
    }
#endif

980 981 982 983 984 985 986 987 988
    // first load the jvm using dlopen
    const char *java_home = NULL;
    void* jvm_lib = _load_jvm(&java_home);

    if (!jvm_lib) {
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "Wasn't able to load JVM\n");
        return 0;
    }

Petri Hintukainen's avatar
Petri Hintukainen committed
989 990 991 992 993 994
    BDJAVA* bdjava = calloc(1, sizeof(BDJAVA));
    if (!bdjava) {
        dl_dlclose(jvm_lib);
        return NULL;
    }

995 996
    JNIEnv* env = NULL;
    JavaVM *jvm = NULL;
Petri Hintukainen's avatar
Petri Hintukainen committed
997
    const char *jar[2] = { storage->classpath[0], storage->classpath[1] };
998
    if (!_find_jvm(jvm_lib, &env, &jvm) &&
Petri Hintukainen's avatar
Petri Hintukainen committed
999
        !_create_jvm(jvm_lib, java_home, jar, &env, &jvm)) {
1000

Petri Hintukainen's avatar
Petri Hintukainen committed
1001
        X_FREE(bdjava);
1002
        dl_dlclose(jvm_lib);
1003 1004
        return NULL;
    }
1005

Marvin Scholz's avatar
Marvin Scholz committed
1006 1007 1008
#if defined(__APPLE__) && !defined(HAVE_BDJ_J2ME)
    bdjava->h_libjli = jli_lib;
#endif
1009 1010 1011
    bdjava->h_libjvm = jvm_lib;
    bdjava->jvm = jvm;

Petri Hintukainen's avatar
Petri Hintukainen committed
1012 1013 1014 1015 1016
    if (debug_mask & DBG_JNI) {
        int version = (int)(*env)->GetVersion(env);
        BD_DEBUG(DBG_BDJ, "Java version: %d.%d\n", version >> 16, version & 0xffff);
    }

1017
    if (!_bdj_init(env, bd, path, bdj_disc_id, storage)) {
1018 1019 1020
        bdj_close(bdjava);
        return NULL;
    }
1021

1022 1023 1024
    /* detach java main thread (CreateJavaVM attachs calling thread to JVM) */
    (*bdjava->jvm)->DetachCurrentThread(bdjava->jvm);

1025
    return bdjava;
1026 1027
}

1028 1029
void bdj_close(BDJAVA *bdjava)
{
1030
    JNIEnv *env;
1031
    int attach = 0;
1032 1033 1034
    jclass shutdown_class;
    jmethodID shutdown_id;

1035 1036 1037 1038
    if (!bdjava) {
        return;
    }

Petri Hintukainen's avatar
Petri Hintukainen committed
1039 1040
    BD_DEBUG(DBG_BDJ, "bdj_close()\n");

1041
    if (bdjava->jvm) {
1042
        if ((*bdjava->jvm)->GetEnv(bdjava->jvm, (void**)&env, JNI_VERSION_1_4) != JNI_OK) {
Petri Hintukainen's avatar
Petri Hintukainen committed
1043 1044 1045
            (*bdjava->jvm)->AttachCurrentThread(bdjava->jvm, (void**)&env, NULL);
            attach = 1;
        }
1046

1047
        if (_get_method(env, &shutdown_class, &shutdown_id,
Petri Hintukainen's avatar
Petri Hintukainen committed
1048 1049
                           "org/videolan/Libbluray", "shutdown", "()V")) {
            (*env)->CallStaticVoidMethod(env, shutdown_class, shutdown_id);
1050 1051 1052

            if ((*env)->ExceptionOccurred(env)) {
                (*env)->ExceptionDescribe(env);
1053
                BD_DEBUG(DBG_BDJ | DBG_CRIT, "Failed to shutdown BD-J (uncaught exception)\n");
1054 1055 1056
                (*env)->ExceptionClear(env);
            }

Petri Hintukainen's avatar
Petri Hintukainen committed
1057 1058
            (*env)->DeleteLocalRef(env, shutdown_class);
        }
1059

1060 1061
        bdj_unregister_native_methods(env);

Petri Hintukainen's avatar
Petri Hintukainen committed
1062 1063 1064
        if (attach) {
            (*bdjava->jvm)->DetachCurrentThread(bdjava->jvm);
        }
1065
    }
Petri Hintukainen's avatar
Petri Hintukainen committed
1066

1067
    if (bdjava->h_libjvm) {
Petri Hintukainen's avatar
Petri Hintukainen committed
1068
        dl_dlclose(bdjava->h_libjvm);
1069
    }
1070

Marvin Scholz's avatar
Marvin Scholz committed
1071 1072 1073 1074 1075 1076
#if defined(__APPLE__) && !defined(HAVE_BDJ_J2ME)
    if (bdjava->h_libjli) {
        dl_dlclose(bdjava->h_libjli);
    }
#endif

1077
    X_FREE(bdjava);
Petri Hintukainen's avatar
Petri Hintukainen committed
1078 1079
}

1080
int bdj_process_event(BDJAVA *bdjava, unsigned ev, unsigned param)
Petri Hintukainen's avatar
Petri Hintukainen committed
1081
{
Petri Hintukainen's avatar
Petri Hintukainen committed
1082
    static const char * const ev_name[] = {
Petri Hintukainen's avatar
Petri Hintukainen committed
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105
        /*  0 */ "NONE",

        /*  1 */ "START",
        /*  2 */ "STOP",
        /*  3 */ "PSR102",

        /*  4 */ "PLAYLIST",
        /*  5 */ "PLAYITEM",
        /*  6 */ "CHAPTER",
        /*  7 */ "MARK",
        /*  8 */ "PTS",
        /*  9 */ "END_OF_PLAYLIST",

        /* 10 */ "SEEK",
        /* 11 */ "RATE",

        /* 12 */ "ANGLE",
        /* 13 */ "AUDIO_STREAM",
        /* 14 */ "SUBTITLE",
        /* 15 */ "SECONDARY_STREAM",

        /* 16 */ "VK_KEY",
        /* 17 */ "UO_MASKED",
Petri Hintukainen's avatar
Petri Hintukainen committed
1106
        /* 18 */ "MOUSE",
Petri Hintukainen's avatar
Petri Hintukainen committed
1107 1108
    };

1109 1110
    JNIEnv* env;
    int attach = 0;
1111 1112
    jclass event_class;
    jmethodID event_id;
1113
    int result = -1;
Petri Hintukainen's avatar
Petri Hintukainen committed
1114

1115
    if (!bdjava) {
1116
        return -1;
1117 1118
    }

Petri Hintukainen's avatar
Petri Hintukainen committed
1119
    if (ev > BDJ_EVENT_LAST) {
1120 1121
        BD_DEBUG(DBG_BDJ | DBG_CRIT, "bdj_process_event(%d,%d): unknown event\n", ev, param);
    }
1122
    // Disable too verbose logging (PTS)
1123
    else if (ev != BDJ_EVENT_PTS) {
1124 1125
        BD_DEBUG(DBG_BDJ, "bdj_process_event(%s,%d)\n", ev_name[ev], param);
    }
Petri Hintukainen's avatar
Petri Hintukainen committed
1126

1127
    if ((*bdjava->jvm)->GetEnv(bdjava->jvm, (void**)&env, JNI_VERSION_1_4) != JNI_OK) {
1128 1129 1130 1131
        (*bdjava->jvm)->AttachCurrentThread(bdjava->jvm, (void**)&env, NULL);
        attach = 1;
    }

1132
    if (_get_method(env, &event_class, &event_id,
1133
                       "org/videolan/Libbluray", "processEvent", "(II)Z")) {
1134
        if ((*env)->CallStaticBooleanMethod(env, event_class, event_id, (jint)ev, (jint)param)) {
1135 1136
            result = 0;
        }
1137 1138 1139

        if ((*env)->ExceptionOccurred(env)) {
            (*env)->ExceptionDescribe(env);
1140
            BD_DEBUG(DBG_BDJ | DBG_CRIT, "bdj_process_event(%u,%u) failed (uncaught exception)\n", ev, param);
1141 1142 1143
            (*env)->ExceptionClear(env);
        }

1144
        (*env)->DeleteLocalRef(env, event_class);
1145
    }
1146 1147 1148 1149

    if (attach) {
        (*bdjava->jvm)->DetachCurrentThread(bdjava->jvm);
    }
1150 1151

    return result;
Petri Hintukainen's avatar
Petri Hintukainen committed
1152
}