Skip to content

KeyboardMap::releaseKeyCharacterMap() detaches JNI threads it did not attach #1743

Description

@QiuYucheng2003

Describe the bug
KeyboardMap::releaseKeyCharacterMap() always calls JavaVM::DetachCurrentThread() after deleting the KeyCharacterMap global ref. getJNIEnvFromJavaVM() only attaches when GetEnv returns JNI_EDETACHED; if the thread is already attached it just returns the existing JNIEnv and does not record that fact.

The documented call site is a NativeActivity command handler (APP_CMD_INIT_WINDOW / APP_CMD_DESTROY). That thread is already attached by android_native_app_glue before android_main runs, so VSG never attaches it, then releaseKeyCharacterMap() detaches the glue’s attach. Later JNI on the same thread (including the glue’s own cleanup DetachCurrentThread) can then see a detached env.

DeleteGlobalRef itself is paired with NewGlobalRef; the bug is only the unconditional detach.

To Reproduce
This is from source inspection of the documented Android path; no runtime log attached.

  1. NativeActivity app using android_native_app_glue.
  2. On APP_CMD_INIT_WINDOW, call KeyboardMap::initializeKeyCharacterMap(app->activity->vm) as documented in Android_Window.h.
  3. On APP_CMD_DESTROY, call KeyboardMap::releaseKeyCharacterMap().
  4. That destroy path detaches the native thread even though VSG did not attach it.

Relevant code:

static JNIEnv* getJNIEnvFromJavaVM(JavaVM* vm)
{
    jint ret = vm->GetEnv((void**)&env, JNI_VERSION_1_6);
    if (ret == JNI_EDETACHED)
        ret = vm->AttachCurrentThread(&env, &args);
    // JNI_OK: already attached, no flag returned
    return env;
}

void KeyboardMap::releaseKeyCharacterMap()
{
    if (classKeyCharacterMap)
    {
        JNIEnv* env = getJNIEnvFromJavaVM(javaVM);
        if (env)
        {
            env->DeleteGlobalRef(classKeyCharacterMap);
            classKeyCharacterMap = nullptr;
        }
        javaVM->DetachCurrentThread(); // always, even if this call did not Attach
    }
}

Expected behavior
Detach only if this code attached the thread. getJNIEnvFromJavaVM should return whether it attached (e.g. bool* attached), and releaseKeyCharacterMap should call DetachCurrentThread only when that flag is true. initializeKeyCharacterMap / getUnicodeChar should follow the same rule.

Screenshots
N/A

Desktop (please complete the following information):

  • OS: N/A (Android native)
  • Browser: N/A
  • Version: current master (Android_Window.cpp KeyboardMap JNI helpers)

Smartphone (please complete the following information):

  • Device: NativeActivity / android_native_app_glue
  • OS: Android
  • Browser: N/A
  • Version: N/A

Additional context
initializeKeyCharacterMap and getUnicodeChar never detach (attach-and-stay). Combined with an unconditional detach in releaseKeyCharacterMap, the intended NativeActivity usage is the mismatched path, not a rare edge case.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions