Search code examples
androidc++bluetoothandroid-ndkjava-native-interface

Getting Android Bluetooth Adapter Name from JNI/C++


The Android API in question is android.bluetooth.BluetoothAdapter, which has a member function getName() which returns the adapter's user friendly name.

In java: BluetoothAdapter.getDefaultAdapter().getName()

I know I can wrap this in a java-function, which I call through jni, but, how can I achieve the same in C++, with only jni/android-ndk?


Solution

  • First of all, you need to have permission to read this value (which you would need regardless of it being native).

    Add to AndroidManifest.xml:

    <uses-permission android:name="android.permission.BLUETOOTH"/>
    

    In native jni-land, things are a bit cumbersome. In short, this is what you need:

    1. Get class android.bluetooth.BluetoothAdapter
    2. Get static method BluetoothAdapter.getDefaultAdapter()
    3. Get method BluetoothAdapter.getName()

    In order to:

    1. Call 2 on 1 to get the default BluetoothAdapter instance
    2. Call getName() on instance from 4. to get the adapter's name.

    This is the same as the java one-liner, just broken down.


    The code (assuming you already have a JNIEnv object):

    // 1. Get class
    // The path matches the package hierarchy of
    // 'android.bluetooth.BluetoothAdapter'
    jclass classBta = env->FindClass("android/bluetooth/BluetoothAdapter");
    
    // 2. Get id of the static method, getDefaultAdapter()
    // Search the web for 'jni function signature' to understand
    // the third argument. In short it's a function that takes no arguments,
    // hence '()', and returns an object of type
    // android.bluetooth.BluetoothAdapter, which uses syntax "L{path};"
    jmethodID methodIdGetAdapter =
        env->GetStaticMethodID(classBta,
                               "getDefaultAdapter",
                               "()Landroid/bluetooth/BluetoothAdapter;");
    
    // 3. Get id of the non-static method, getName()
    // The third argument is the getName function signature,
    // no arguments, and returns a java.lang.String object.
    jmethodID methodIdGetName =
        env->GetMethodID(classBta,
                         "getName",
                         "()Ljava/lang/String;");
    
    // 4. We get the instance returned by getDefaultAdapter()
    jobject objBta = (jobject)
        env->CallStaticObjectMethod(classBta, methodIdGetAdapter);
    
    // 5. Call member method getName on instance
    jstring strName = (jstring)
        env->CallObjectMethod(objBta, methodIdGetName);
    
    // Convert jstring to a regular string
    const char *result = env->GetStringUTFChars(strName, 0);
    std::string blueToothName(result);
    

    For the sake of clarity, I've omitting sensible checks to see if the various functions have succeeded, and also cleanup:

    env->DeleteLocalRef(classBta);
    env->DeleteLocalRef(objBta);
    env->DeleteLocalRef(strName);
    env->ReleaseStringUTFChars(strName, result);