[PATCH v15 0/4] MR10569: wineandroid: rework Android integration and switch to split process model
This series reworks the wineandroid driver and moves it away from the current “Wine inside JVM process” model to a split architecture where the Android activity (UI) and Wine run in separate processes. The existing model relies on running Wine inside the JVM process and interacting with Android through JNI callbacks and APC/unixlib-based dispatch. This approach no longer works reliably on modern Android: - Android 10+ applies seccomp restrictions to app processes, making a number of required syscalls unavailable inside the activity process, while processes started via fork/exec are not subject to the same limitations. - The implementation depends on Wine thread context (TEB), signal handling, and register state assumptions that conflict with the JVM runtime (e.g. segment register usage on x86), requiring fragile workarounds. - Due to sandboxing, it is difficult to reliably debug or even observe all failure modes, and future Android changes are likely to tighten these restrictions further. At the same time, downstream projects such as MiceWine, Winlator, Termux’s Wine environment, GameHub and GameNative already use a model where the UI (activity) and Wine run in separate processes and communicate via IPC, which has proven to work reliably on current Android systems. This series implements that model in wineandroid: - Wine is now started in a separate process via fork/exec instead of running inside the JVM process. - Android-facing code no longer depends on the Wine APC/unixlib execution path and runs on the UI thread where appropriate (e.g. native window registration). - ioctl handling is reworked to remove reliance on Wine thread context, with explicit JNIEnv usage and fd-based reply plumbing. - Environment setup and initialization are moved to the Java side, simplifying the native startup path. - Logging for ioctl paths is routed through Android logcat without depending on a Wine TEB. The changes are split into multiple bisect-safe commits to keep intermediate states functional while migrating the code step by step. With Wine running in a separate process, it is no longer subject to JVM seccomp restrictions, and there are no longer conflicts between JVM and Wine execution contexts (signals, thread state, register usage, etc.). <details> <summary>Old message</summary> wineandroid: move ioctl handling to dedicated Java-only thread Move ioctl handling out of the Wine APC/unixlib execution path into a dedicated Java-only thread without Wine context. On Android 10+ a number of syscalls are restricted inside the activity process via seccomp, while processes started via fork/exec are not subject to the same limitations. Decoupling ioctl handling from the Wine context is required to eventually run this code outside of the activity process. This change introduces a new transport and execution model where ioctl dispatch no longer depends on Wine thread state, preparing for moving the Android backend into a separate process. The refactor is intentionally split into multiple bisect-safe commits to keep intermediate states functional while gradually migrating the dispatch path. Since this is a fairly large chunk of work, I’d like to make sure we’re aligned before continuing. </details> -- v15: wineandroid: split Android driver and Wine into separate processes wineandroid: terminate activity when desktop client disconnects wineandroid: make JNI initialization work with direct JVM loading wineandroid: create desktop view via ioctl and pass event pipe https://gitlab.winehq.org/wine/wine/-/merge_requests/10569
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Move desktop view creation and event pipe setup to a dedicated ioctl. The dispatch thread now creates the pipe and returns the read end to the caller, while keeping the write end locally. This makes ownership of the event path explicit and prepares it to be transferred cleanly through the transport. Also pass the Wine debug channel state together with the request so the dispatch side can preserve the caller's logging configuration. This prepares the event path for the upcoming process split. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/wineandroid.drv/android.h | 1 + dlls/wineandroid.drv/device.c | 42 +++++++++++++++++++++++++++++----- dlls/wineandroid.drv/window.c | 22 ++++++++---------- 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/dlls/wineandroid.drv/android.h b/dlls/wineandroid.drv/android.h index c79ce31a046..853c8318601 100644 --- a/dlls/wineandroid.drv/android.h +++ b/dlls/wineandroid.drv/android.h @@ -82,6 +82,7 @@ extern UINT ANDROID_OpenGLInit( UINT version, const struct opengl_funcs *opengl_ */ extern void start_android_device(void); +extern void createDesktopView( int *event_source ); extern void register_native_window( HWND hwnd, struct ANativeWindow *win, BOOL client ); extern struct ANativeWindow *create_ioctl_window( HWND hwnd, BOOL opengl ); extern struct ANativeWindow *grab_ioctl_window( struct ANativeWindow *window ); diff --git a/dlls/wineandroid.drv/device.c b/dlls/wineandroid.drv/device.c index 34424009afd..0a7642d95ab 100644 --- a/dlls/wineandroid.drv/device.c +++ b/dlls/wineandroid.drv/device.c @@ -79,6 +79,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(android); static HWND capture_window; static HWND desktop_window; +int event_sink = -1; static pthread_mutex_t dispatch_ioctl_lock = PTHREAD_MUTEX_INITIALIZER; #define ANDROIDCONTROLTYPE ((ULONG)'A') @@ -86,6 +87,7 @@ static pthread_mutex_t dispatch_ioctl_lock = PTHREAD_MUTEX_INITIALIZER; enum android_ioctl { + IOCTL_CREATE_DESKTOP_VIEW, IOCTL_CREATE_WINDOW, IOCTL_DESTROY_WINDOW, IOCTL_WINDOW_POS_CHANGED, @@ -147,6 +149,12 @@ struct ioctl_header BOOL opengl; }; +struct ioctl_android_create_desktop_view +{ + struct ioctl_header hdr; + int log_flags; +}; + struct ioctl_android_create_window { struct ioctl_header hdr; @@ -467,14 +475,32 @@ static jobject load_java_method( JNIEnv* env, jmethodID *method, const char *nam return java_object; } -static void create_desktop_view( JNIEnv* env ) +static int createDesktopView_ioctl( JNIEnv* env, void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size, int *reply_fd ) { + static int event_pipe[2]; static jmethodID method; jobject object; + struct ioctl_android_create_desktop_view *res = data; + + if (in_size < sizeof(*res)) return -EINVAL; + + if (event_sink != -1) close(event_sink); + + if (pipe2( event_pipe, O_CLOEXEC | O_NONBLOCK ) == -1) + { + LOG( ERR, "could not create data event pipe\n" ); + return -1; + } + + event_sink = event_pipe[1]; + *reply_fd = event_pipe[0]; + + log_flags = res->log_flags; // Copy logging levels from client - if (!(object = load_java_method( env, &method, "createDesktopView", "()V" ))) return; + if (!(object = load_java_method( env, &method, "createDesktopView", "()V" ))) return -ENOSYS; (*env)->CallVoidMethod( env, object, method ); + return 0; } static int createWindow_ioctl( JNIEnv* env, void *data, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size, int *reply_fd ) @@ -821,6 +847,7 @@ static int setCursor_ioctl( JNIEnv* env, void *data, DWORD in_size, DWORD out_si typedef int (*ioctl_func)( JNIEnv* env, void *in, DWORD in_size, DWORD out_size, ULONG_PTR *ret_size, int *reply_fd ); static const ioctl_func ioctl_funcs[] = { + createDesktopView_ioctl, /* IOCTL_CREATE_DESKTOP_VIEW */ createWindow_ioctl, /* IOCTL_CREATE_WINDOW */ destroyWindow_ioctl, /* IOCTL_DESTROY_WINDOW */ windowPosChanged_ioctl, /* IOCTL_WINDOW_POS_CHANGED */ @@ -993,8 +1020,6 @@ static void *bootstrap_looper_thread( void *arg ) abort(); } - create_desktop_view( env ); - (*java_vm)->DetachCurrentThread( java_vm ); return NULL; } @@ -1003,8 +1028,6 @@ void start_android_device(void) { pthread_t t; - log_flags = __wine_dbg_get_channel_flags(&__wine_dbch_android); - /* Use a temporary bootstrap thread to request the main thread looper * without interfering with the current Wine/JVM execution context * (including register and thread-state assumptions). Actual ioctl @@ -1095,6 +1118,13 @@ static void win_decRef( struct android_native_base_t *base ) InterlockedDecrement( &win->ref ); } +void createDesktopView( int *event_source ) +{ + struct ioctl_android_create_desktop_view res = { 0 }; + res.log_flags = __wine_dbg_get_channel_flags(&__wine_dbch_android); + android_ioctl( IOCTL_CREATE_DESKTOP_VIEW, &res, sizeof(res), NULL, NULL, event_source ); +} + static int dequeueBuffer( struct ANativeWindow *window, struct ANativeWindowBuffer **buffer, int *fence ) { struct native_win_wrapper *win = (struct native_win_wrapper *)window; diff --git a/dlls/wineandroid.drv/window.c b/dlls/wineandroid.drv/window.c index b3f3aeea955..98a0e3dd066 100644 --- a/dlls/wineandroid.drv/window.c +++ b/dlls/wineandroid.drv/window.c @@ -173,9 +173,11 @@ struct java_event static struct list event_queue = LIST_INIT( event_queue ); static struct java_event *current_event; -static int event_pipe[2]; +static int event_source; static DWORD desktop_tid; +extern int event_sink; + /*********************************************************************** * send_event */ @@ -183,7 +185,7 @@ int send_event( const union event_data *data ) { int res; - if ((res = write( event_pipe[1], data, sizeof(*data) )) != sizeof(*data)) + if ((res = write( event_sink, data, sizeof(*data) )) != sizeof(*data)) { p__android_log_print( ANDROID_LOG_ERROR, "wine", "failed to send event" ); return -1; @@ -343,12 +345,7 @@ static void init_event_queue(void) HANDLE handle; int ret; - if (pipe2( event_pipe, O_CLOEXEC | O_NONBLOCK ) == -1) - { - ERR( "could not create data\n" ); - NtTerminateProcess( 0, 1 ); - } - if (wine_server_fd_to_handle( event_pipe[0], GENERIC_READ | SYNCHRONIZE, 0, &handle )) + if (wine_server_fd_to_handle( event_source, GENERIC_READ | SYNCHRONIZE, 0, &handle )) { ERR( "Can't allocate handle for event fd\n" ); NtTerminateProcess( 0, 1 ); @@ -383,7 +380,7 @@ static void pull_events(void) { if (!(event = malloc( sizeof(*event) ))) break; - res = read( event_pipe[0], &event->data, sizeof(event->data) ); + res = read( event_source, &event->data, sizeof(event->data) ); if (res != sizeof(event->data)) break; list_add_tail( &event_queue, &event->entry ); } @@ -515,7 +512,7 @@ static int process_events( DWORD mask ) next = LIST_ENTRY( event_queue.next, struct java_event, entry ); } current_event = previous; - return list_empty( &event_queue ) && !check_fd_events( event_pipe[0], POLLIN ); + return list_empty( &event_queue ) && !check_fd_events( event_source, POLLIN ); } @@ -531,7 +528,7 @@ static int wait_events( int timeout ) struct pollfd pollfd; int ret; - pollfd.fd = event_pipe[0]; + pollfd.fd = event_source; pollfd.events = POLLIN | POLLHUP; ret = poll( &pollfd, 1, timeout ); if (ret == -1 && errno == EINTR) continue; @@ -1238,8 +1235,9 @@ BOOL has_client_surface( HWND hwnd ) */ BOOL ANDROID_CreateDesktop( const WCHAR *name, UINT width, UINT height ) { - init_event_queue(); start_android_device(); + createDesktopView( &event_source ); + init_event_queue(); /* wait until we receive the surface changed event */ while (!screen_width) { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10569
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Adjust the JNI initialization order so wineandroid.so can be loaded directly by the JVM. Load the required native libraries explicitly from Java with System.load() in dependency order, avoiding reliance on LD_LIBRARY_PATH tricks for transitive dependency resolution. This prepares the Android startup path for the upcoming process split while keeping the transition bisect-safe. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/wineandroid.drv/WineActivity.java | 3 ++- dlls/wineandroid.drv/init.c | 36 ++++++++++++++------------ 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/dlls/wineandroid.drv/WineActivity.java b/dlls/wineandroid.drv/WineActivity.java index 1ee99b8a037..23b06386652 100644 --- a/dlls/wineandroid.drv/WineActivity.java +++ b/dlls/wineandroid.drv/WineActivity.java @@ -165,7 +165,8 @@ private void loadWine( String cmdline ) createProgressDialog( 0, "Setting up the Windows environment..." ); - System.load( dlldir.toString() + get_so_dir(wine_abi) + "/ntdll.so" ); + for ( String lib : new String[] { "ntdll.so", "win32u.so", "wineandroid.so" } ) + System.load( dlldir.toString() + get_so_dir(wine_abi) + "/" + lib ); prefix.mkdirs(); runWine( loader.toString(), cmdline ); diff --git a/dlls/wineandroid.drv/init.c b/dlls/wineandroid.drv/init.c index b5732a12bc1..88565dd258d 100644 --- a/dlls/wineandroid.drv/init.c +++ b/dlls/wineandroid.drv/init.c @@ -39,6 +39,10 @@ #include "wine/server.h" #include "wine/debug.h" +#ifndef WINE_JAVA_CLASS +#define WINE_JAVA_CLASS "org/winehq/wine/WineActivity" +#endif + WINE_DEFAULT_DEBUG_CHANNEL(android); unsigned int screen_width = 0; @@ -395,8 +399,6 @@ static void load_android_libs(void) NTSTATUS __wine_unix_lib_init(void) { pthread_mutexattr_t attr; - jclass class; - JNIEnv *jni_env; load_android_libs(); @@ -405,21 +407,21 @@ NTSTATUS __wine_unix_lib_init(void) pthread_mutex_init( &win_data_mutex, &attr ); pthread_mutexattr_destroy( &attr ); - if (java_vm) /* running under Java */ - { -#ifdef __i386__ - WORD old_fs; - __asm__( "mov %%fs,%0" : "=r" (old_fs) ); -#endif - (*java_vm)->AttachCurrentThread( java_vm, &jni_env, 0 ); - class = (*jni_env)->GetObjectClass( jni_env, java_object ); - (*jni_env)->RegisterNatives( jni_env, class, methods, ARRAY_SIZE( methods )); - (*jni_env)->DeleteLocalRef( jni_env, class ); -#ifdef __i386__ - /* the Java VM hijacks %fs for its own purposes, restore it */ - __asm__( "mov %0,%%fs" :: "r" (old_fs) ); -#endif - } __wine_set_user_driver( &android_drv_funcs, WINE_GDI_DRIVER_VERSION ); return STATUS_SUCCESS; } + +jint JNI_OnLoad( JavaVM *vm, void *reserved ) +{ + JNIEnv *env; + jclass class; + + load_android_libs(); + + java_vm = vm; + if ((*vm)->AttachCurrentThread( vm, &env, NULL ) != JNI_OK) return JNI_ERR; + if (!(class = (*env)->FindClass( env, WINE_JAVA_CLASS ))) return JNI_ERR; + (*env)->RegisterNatives( env, class, methods, ARRAY_SIZE( methods )); + (*env)->DeleteLocalRef( env, class ); + return JNI_VERSION_1_6; +} -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10569
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Track the client connection that creates the desktop view and treat it as the lifetime owner of the Android activity. When this client (typically explorer.exe) disconnects, terminate the process to avoid leaving the activity running without an associated desktop process, which would otherwise result in a stuck or unresponsive UI. This matches the intended 1:1 relationship between the activity and the Wine desktop process and ensures that all associated resources are cleaned up when the desktop client exits. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/wineandroid.drv/device.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dlls/wineandroid.drv/device.c b/dlls/wineandroid.drv/device.c index 0a7642d95ab..4ec48368901 100644 --- a/dlls/wineandroid.drv/device.c +++ b/dlls/wineandroid.drv/device.c @@ -72,6 +72,8 @@ static int log_flags; WINE_DEFAULT_DEBUG_CHANNEL(android); +static int desktop_client_fd = -1; + #ifndef SYNC_IOC_WAIT #define SYNC_IOC_WAIT _IOW('>', 0, __s32) #endif @@ -910,6 +912,8 @@ static int handle_ioctl_message( JNIEnv *env, int fd ) { pthread_mutex_lock( &dispatch_ioctl_lock ); status = ioctl_funcs[code]( env, buffer, ret, sizeof(buffer), &reply_size, &reply_fd ); + if (IOCTL_CREATE_DESKTOP_VIEW == code) // special case: desktop client + desktop_client_fd = fd; pthread_mutex_unlock( &dispatch_ioctl_lock ); } } @@ -949,6 +953,8 @@ static int looper_handle_client( int fd, int events, void *data ) { pALooper_removeFd( looper, fd ); close( fd ); + if (fd == desktop_client_fd) // our explorer process died + _exit(0); } break; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10569
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Move the Android JNI startup entry point from ntdll to wineandroid.drv and use it to initialize the Android-facing driver side in the JVM process. WineActivity now starts the Wine process separately, while the JVM process keeps only the Android driver side. This follows the same client/server-style separation as winex11 and winewayland, where Wine runs as a client of an external display server. With Wine now running in its own process, it is no longer subject to the seccomp restrictions imposed on the JVM process and does not interfere with the JVM runtime context (signals, thread state, register usage, etc.). The old Android-specific JNI bootstrap code is removed from ntdll. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/ntdll/unix/loader.c | 106 ------------------------- dlls/wineandroid.drv/WineActivity.java | 36 +++++---- dlls/wineandroid.drv/android.h | 5 +- dlls/wineandroid.drv/device.c | 53 ++----------- dlls/wineandroid.drv/init.c | 9 ++- dlls/wineandroid.drv/window.c | 3 +- 6 files changed, 37 insertions(+), 175 deletions(-) diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index c1e6b588dcb..c1321ce679d 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -77,9 +77,6 @@ #else extern char **environ; #endif -#ifdef __ANDROID__ -# include <jni.h> -#endif #include "ntstatus.h" #include "windef.h" @@ -1874,109 +1871,6 @@ static void start_main_thread(void) server_init_process_done(); } -#ifdef __ANDROID__ - -#ifndef WINE_JAVA_CLASS -#define WINE_JAVA_CLASS "org/winehq/wine/WineActivity" -#endif - -DECLSPEC_EXPORT JavaVM *java_vm = NULL; -DECLSPEC_EXPORT jobject java_object = 0; -DECLSPEC_EXPORT unsigned short java_gdt_sel = 0; - -/* main Wine initialisation */ -static jstring wine_init_jni( JNIEnv *env, jobject obj, jobjectArray cmdline ) -{ - char **argv; - char *str; - char error[1024], *winedebuglog = NULL; - int i, argc, length; - void (*update_func)( const char * ); - - /* get the command line array */ - - argc = (*env)->GetArrayLength( env, cmdline ); - for (i = length = 0; i < argc; i++) - { - jobject str_obj = (*env)->GetObjectArrayElement( env, cmdline, i ); - length += (*env)->GetStringUTFLength( env, str_obj ) + 1; - } - - argv = malloc( (argc + 1) * sizeof(*argv) + length ); - str = (char *)(argv + argc + 1); - for (i = 0; i < argc; i++) - { - jobject str_obj = (*env)->GetObjectArrayElement( env, cmdline, i ); - length = (*env)->GetStringUTFLength( env, str_obj ); - (*env)->GetStringUTFRegion( env, str_obj, 0, - (*env)->GetStringLength( env, str_obj ), str ); - argv[i] = str; - str[length] = 0; - str += length + 1; - } - argv[argc] = NULL; - - /* set the environment variables */ - - // Activity always modifies LD_LIBRARY_PATH in order to load libraries - // from custom location in JVM process. - update_func = dlsym( RTLD_DEFAULT, "android_update_LD_LIBRARY_PATH" ); - if (update_func) update_func( getenv("LD_LIBRARY_PATH") ); - - winedebuglog = getenv("WINEDEBUGLOG"); - if (winedebuglog) - { - int fd = open( winedebuglog, O_WRONLY | O_CREAT | O_APPEND, 0666 ); - if (fd != -1) - { - dup2( fd, 2 ); - close( fd ); - } - } - - java_object = (*env)->NewGlobalRef( env, obj ); - - main_argc = argc; - main_argv = argv; - - init_paths(); - init_environment(); - -#ifdef __i386__ - { - unsigned short java_fs; - __asm__( "mov %%fs,%0" : "=r" (java_fs) ); - if (!(java_fs & 4)) java_gdt_sel = java_fs; - __asm__( "mov %0,%%fs" :: "r" (0) ); - start_main_thread(); - __asm__( "mov %0,%%fs" :: "r" (java_fs) ); - } -#else - start_main_thread(); -#endif - return (*env)->NewStringUTF( env, error ); -} - -jint JNI_OnLoad( JavaVM *vm, void *reserved ) -{ - static const JNINativeMethod method = - { - "wine_init", "([Ljava/lang/String;)Ljava/lang/String;", wine_init_jni - }; - - JNIEnv *env; - jclass class; - - virtual_init(); - - java_vm = vm; - if ((*vm)->AttachCurrentThread( vm, &env, NULL ) != JNI_OK) return JNI_ERR; - if (!(class = (*env)->FindClass( env, WINE_JAVA_CLASS ))) return JNI_ERR; - (*env)->RegisterNatives( env, class, &method, 1 ); - return JNI_VERSION_1_6; -} - -#endif /* __ANDROID__ */ #ifdef __APPLE__ static void *apple_wine_thread( void *arg ) diff --git a/dlls/wineandroid.drv/WineActivity.java b/dlls/wineandroid.drv/WineActivity.java index 23b06386652..bf2408b4410 100644 --- a/dlls/wineandroid.drv/WineActivity.java +++ b/dlls/wineandroid.drv/WineActivity.java @@ -61,8 +61,7 @@ public class WineActivity extends Activity { - private native String wine_init( String[] cmdline ); - private native void wine_looper_init(); + private native void wine_init(); public native void wine_desktop_changed( int width, int height ); public native void wine_config_changed( int dpi ); public native void wine_surface_changed( int hwnd, Surface surface, boolean opengl ); @@ -138,6 +137,7 @@ private void loadWine( String cmdline ) File dlldir = new File( libdir, "wine" ); File prefix = new File( getFilesDir(), "prefix" ); File loader = new File( dlldir, get_so_dir(wine_abi) + "/wine" ); + File log = null; String locale = Locale.getDefault().getLanguage() + "_" + Locale.getDefault().getCountry() + ".UTF-8"; @@ -156,7 +156,7 @@ private void loadWine( String cmdline ) if (winedebug == null) winedebug = readFileString( new File( getFilesDir(), "winedebug" )); if (winedebug != null) { - File log = new File( getFilesDir(), "log" ); + log = new File( getFilesDir(), "log" ); putenv( "WINEDEBUG", winedebug ); putenv( "WINEDEBUGLOG", log.toString() ); Log.i( LOGTAG, "logging to " + log.toString() ); @@ -169,18 +169,32 @@ private void loadWine( String cmdline ) System.load( dlldir.toString() + get_so_dir(wine_abi) + "/" + lib ); prefix.mkdirs(); - runWine( loader.toString(), cmdline ); + runWine( loader.toString(), cmdline, log ); } - private final void runWine( String loader, String cmdline ) + private final void runWine( String loader, String cmdline, File log ) { + CountDownLatch latch = new CountDownLatch(1); String[] cmd = { loader, "c:\\windows\\system32\\explorer.exe", "/desktop=shell,,android", cmdline }; - String err = wine_init( cmd ); - Log.e( LOGTAG, err ); + runOnUiThread( new Runnable() { public void run() { + try { wine_init(); } finally { latch.countDown(); } + }}); + try { latch.await(); } catch ( Exception e ) {} + + try { + new ProcessBuilder(cmd) + .redirectErrorStream(true) + .redirectOutput(ProcessBuilder.Redirect.appendTo( + log != null ? log : new File("/dev/null") + )) + .start(); + } catch (IOException e) { + Log.e("WineError", "Failed to exec " + String.join(" ", cmd) + ": " + e); + } } private void createProgressDialog( final int max, final String message ) @@ -893,12 +907,4 @@ public void windowPosChanged( final int hwnd, final int flags, final int insert_ public void run() { window_pos_changed( hwnd, flags, insert_after, owner, style, window_rect, client_rect, visible_rect ); }} ); } - - private void obtainLooper() { - CountDownLatch latch = new CountDownLatch(1); - runOnUiThread( new Runnable() { public void run() { - try { wine_looper_init(); } finally { latch.countDown(); } - }}); - try { latch.await(); } catch ( Exception e ) {} - } } diff --git a/dlls/wineandroid.drv/android.h b/dlls/wineandroid.drv/android.h index 853c8318601..cc558aa01f6 100644 --- a/dlls/wineandroid.drv/android.h +++ b/dlls/wineandroid.drv/android.h @@ -81,7 +81,6 @@ extern UINT ANDROID_OpenGLInit( UINT version, const struct opengl_funcs *opengl_ * Android pseudo-device */ -extern void start_android_device(void); extern void createDesktopView( int *event_source ); extern void register_native_window( HWND hwnd, struct ANativeWindow *win, BOOL client ); extern struct ANativeWindow *create_ioctl_window( HWND hwnd, BOOL opengl ); @@ -139,6 +138,7 @@ extern void update_keyboard_lock_state( WORD vkey, UINT state ); /* JNI entry points */ extern void looper_init( JNIEnv *env, jobject obj ); +extern void wine_init_jni( JNIEnv *env, jobject obj ); extern void desktop_changed( JNIEnv *env, jobject obj, jint width, jint height ); extern void config_changed( JNIEnv *env, jobject obj, jint dpi ); extern void surface_changed( JNIEnv *env, jobject obj, jint win, jobject surface, @@ -196,8 +196,7 @@ union event_data int send_event( const union event_data *data ); -extern JavaVM *java_vm; -extern jobject java_object; +extern int event_source; /* string helpers */ diff --git a/dlls/wineandroid.drv/device.c b/dlls/wineandroid.drv/device.c index 4ec48368901..cdfa7847454 100644 --- a/dlls/wineandroid.drv/device.c +++ b/dlls/wineandroid.drv/device.c @@ -73,6 +73,7 @@ static int log_flags; WINE_DEFAULT_DEBUG_CHANNEL(android); static int desktop_client_fd = -1; +static jobject java_object; #ifndef SYNC_IOC_WAIT #define SYNC_IOC_WAIT _IOW('>', 0, __s32) @@ -867,17 +868,6 @@ static const ioctl_func ioctl_funcs[] = static ALooper *looper; static JNIEnv *looper_env; /* JNIEnv for the main thread looper. Must only be used from that thread. */ -void looper_init( JNIEnv* env, jobject obj ) -{ - looper_env = env; - if (!(looper = pALooper_forThread())) - { - LOG( ERR, "No looper for current thread\n" ); - abort(); - } - pALooper_acquire( looper ); -} - /* Handle a single ioctl request from a client socket. * Returns 0 if a request was handled successfully and the caller may * continue draining the socket, -1 if there is nothing more to read @@ -985,25 +975,19 @@ static int looper_handle_listen( int fd, int events, void *data ) return 1; } -static void *bootstrap_looper_thread( void *arg ) +/* main Wine initialisation */ +void wine_init_jni( JNIEnv *env, jobject obj ) { - JNIEnv *env; - jmethodID method = NULL; - jobject object = NULL; int sockfd; - if (!java_vm || (*java_vm)->AttachCurrentThread( java_vm, &env, 0 ) != JNI_OK) - { - LOG( ERR, "Failed to attach current thread\n" ); - return NULL; - } - - if (!(object = load_java_method( env, &method, "obtainLooper", "()V" ))) + java_object = (*env)->NewGlobalRef( env, obj ); + looper_env = env; + if (!(looper = pALooper_forThread())) { - LOG( ERR, "Failed to obtain looper\n" ); + LOG( ERR, "No looper for current thread\n"); abort(); } - (*env)->CallVoidMethod( env, object, method ); + pALooper_acquire( looper ); sockfd = socket( AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0 ); if (sockfd < 0) @@ -1025,27 +1009,6 @@ static void *bootstrap_looper_thread( void *arg ) close(sockfd); abort(); } - - (*java_vm)->DetachCurrentThread( java_vm ); - return NULL; -} - -void start_android_device(void) -{ - pthread_t t; - - /* Use a temporary bootstrap thread to request the main thread looper - * without interfering with the current Wine/JVM execution context - * (including register and thread-state assumptions). Actual ioctl - * dispatch then runs from the main thread looper. - */ - if (!pthread_create( &t, NULL, bootstrap_looper_thread, NULL )) - pthread_join(t, NULL); - else - { - LOG( ERR, "Failed to spawn looper bootstrap thread\n" ); - abort(); - } } diff --git a/dlls/wineandroid.drv/init.c b/dlls/wineandroid.drv/init.c index 88565dd258d..df0cb2b72ec 100644 --- a/dlls/wineandroid.drv/init.c +++ b/dlls/wineandroid.drv/init.c @@ -26,6 +26,8 @@ #include "config.h" +#include <fcntl.h> +#include <unistd.h> #include <stdarg.h> #include <string.h> #include <dlfcn.h> @@ -87,7 +89,7 @@ void init_monitors( int width, int height ) wine_dbgstr_rect( &rect ), wine_dbgstr_rect( &monitor_rc_work )); /* if we're notified from Java thread, update registry */ - if (java_vm) NtUserCallNoParam( NtUserCallNoParam_DisplayModeChanged ); + if (event_source != -1) NtUserCallNoParam( NtUserCallNoParam_DisplayModeChanged ); } @@ -171,7 +173,7 @@ void set_screen_dpi( DWORD dpi ) */ static void fetch_display_metrics(void) { - if (java_vm) return; /* for Java threads it will be set when the top view is created */ + if (event_source != -1) return; /* for Java threads it will be set when the top view is created */ SERVER_START_REQ( get_window_rectangles ) { @@ -328,12 +330,12 @@ static const struct user_driver_funcs android_drv_funcs = static const JNINativeMethod methods[] = { - { "wine_looper_init", "()V", looper_init }, { "wine_desktop_changed", "(II)V", desktop_changed }, { "wine_config_changed", "(I)V", config_changed }, { "wine_surface_changed", "(ILandroid/view/Surface;Z)V", surface_changed }, { "wine_motion_event", "(IIIIII)Z", motion_event }, { "wine_keyboard_event", "(IIII)Z", keyboard_event }, + { "wine_init", "()V", wine_init_jni } }; #define DECL_FUNCPTR(f) typeof(f) * p##f = NULL @@ -418,7 +420,6 @@ jint JNI_OnLoad( JavaVM *vm, void *reserved ) load_android_libs(); - java_vm = vm; if ((*vm)->AttachCurrentThread( vm, &env, NULL ) != JNI_OK) return JNI_ERR; if (!(class = (*env)->FindClass( env, WINE_JAVA_CLASS ))) return JNI_ERR; (*env)->RegisterNatives( env, class, methods, ARRAY_SIZE( methods )); diff --git a/dlls/wineandroid.drv/window.c b/dlls/wineandroid.drv/window.c index 98a0e3dd066..db9294ee0d1 100644 --- a/dlls/wineandroid.drv/window.c +++ b/dlls/wineandroid.drv/window.c @@ -173,7 +173,7 @@ struct java_event static struct list event_queue = LIST_INIT( event_queue ); static struct java_event *current_event; -static int event_source; +int event_source = -1; static DWORD desktop_tid; extern int event_sink; @@ -1235,7 +1235,6 @@ BOOL has_client_surface( HWND hwnd ) */ BOOL ANDROID_CreateDesktop( const WCHAR *name, UINT width, UINT height ) { - start_android_device(); createDesktopView( &event_source ); init_event_queue(); /* wait until we receive the surface changed event */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10569
@julliard Hi. I reduced the series from 20 commits down to 4, so it should hopefully be much easier to review now. I’d appreciate it if you could take another look when you have time. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10569#note_139920
participants (2)
-
Twaik Yont -
Twaik Yont (@twaik)