Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5b0b6e78 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "Bluetooth: HID: Add support for HID Device Role"

parents 0afc3378 65b596c5
Loading
Loading
Loading
Loading
+8 −0
Original line number Original line Diff line number Diff line
@@ -294,5 +294,13 @@
                <action android:name="android.bluetooth.IBluetoothHandsfreeClient" />
                <action android:name="android.bluetooth.IBluetoothHandsfreeClient" />
            </intent-filter>
            </intent-filter>
        </service>
        </service>
        <service
            android:process="@string/process"
            android:name = ".hid.HidDevService"
            android:enabled="@bool/profile_supported_hidd">
            <intent-filter>
                <action android:name="android.bluetooth.IBluetoothHidDevice" />
            </intent-filter>
        </service>
    </application>
    </application>
</manifest>
</manifest>
+1 −0
Original line number Original line Diff line number Diff line
@@ -9,6 +9,7 @@ LOCAL_SRC_FILES:= \
    com_android_bluetooth_a2dp.cpp \
    com_android_bluetooth_a2dp.cpp \
    com_android_bluetooth_avrcp.cpp \
    com_android_bluetooth_avrcp.cpp \
    com_android_bluetooth_hid.cpp \
    com_android_bluetooth_hid.cpp \
    com_android_bluetooth_hidd.cpp \
    com_android_bluetooth_hdp.cpp \
    com_android_bluetooth_hdp.cpp \
    com_android_bluetooth_pan.cpp \
    com_android_bluetooth_pan.cpp \
    com_android_bluetooth_gatt.cpp
    com_android_bluetooth_gatt.cpp
+2 −0
Original line number Original line Diff line number Diff line
@@ -43,6 +43,8 @@ int register_com_android_bluetooth_avrcp(JNIEnv* env);


int register_com_android_bluetooth_hid(JNIEnv* env);
int register_com_android_bluetooth_hid(JNIEnv* env);


int register_com_android_bluetooth_hidd(JNIEnv* env);

int register_com_android_bluetooth_hdp(JNIEnv* env);
int register_com_android_bluetooth_hdp(JNIEnv* env);


int register_com_android_bluetooth_pan(JNIEnv* env);
int register_com_android_bluetooth_pan(JNIEnv* env);
+5 −0
Original line number Original line Diff line number Diff line
@@ -1176,6 +1176,11 @@ jint JNI_OnLoad(JavaVM *jvm, void *reserved)
        return JNI_ERR;
        return JNI_ERR;
    }
    }


    if ((status = android::register_com_android_bluetooth_hidd(e)) < 0) {
        ALOGE("jni hidd registration failure: %d", status);
        return JNI_ERR;
    }

    if ((status = android::register_com_android_bluetooth_hdp(e)) < 0) {
    if ((status = android::register_com_android_bluetooth_hdp(e)) < 0) {
        ALOGE("jni hdp registration failure: %d", status);
        ALOGE("jni hdp registration failure: %d", status);
        return JNI_ERR;
        return JNI_ERR;
+514 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2013 The Linux Foundation. All rights reserved
 * Not a Contribution.
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "BluetoothHidDevServiceJni"

#define LOG_NDEBUG 0

#define CHECK_CALLBACK_ENV                                                      \
   if (!checkCallbackThread()) {                                                \
       ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);\
       return;                                                                  \
   }

#include "com_android_bluetooth.h"
#include "hardware/bt_hd.h"
#include "utils/Log.h"
#include "android_runtime/AndroidRuntime.h"

#include <string.h>

namespace android {

static jmethodID method_onApplicationStateChanged;
static jmethodID method_onConnectStateChanged;
static jmethodID method_onGetReport;
static jmethodID method_onSetReport;
static jmethodID method_onSetProtocol;
static jmethodID method_onIntrData;
static jmethodID method_onVirtualCableUnplug;


static const bthd_interface_t *sHiddIf = NULL;
static jobject mCallbacksObj = NULL;
static JNIEnv *sCallbackEnv = NULL;

static bool checkCallbackThread() {
    sCallbackEnv = getCallbackEnv();

    if (sCallbackEnv != AndroidRuntime::getJNIEnv() || sCallbackEnv == NULL) {
        return false;
    }

    return true;
}

static void application_state_callback(bt_bdaddr_t *bd_addr, bthd_application_state_t state) {
    jboolean registered = JNI_FALSE;
    jbyteArray addr;

    CHECK_CALLBACK_ENV

    if (state == BTHD_APP_STATE_REGISTERED) {
        registered = JNI_TRUE;
    }

    if (bd_addr) {
        addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
        if (!addr) {
            ALOGE("%s: failed to allocate storage for bt_addr", __FUNCTION__);
            checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
            return;
        }
        sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *) bd_addr);
    } else {
        addr = NULL;
    }

    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onApplicationStateChanged, addr, registered);

    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);

    if (addr) {
        sCallbackEnv->DeleteLocalRef(addr);
    }
}

static void connection_state_callback(bt_bdaddr_t *bd_addr, bthd_connection_state_t state) {
    jbyteArray addr;

    CHECK_CALLBACK_ENV

    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
    if (!addr) {
        ALOGE("%s: failed to allocate storage for bt_addr", __FUNCTION__);
        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
        return;
    }
    sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *) bd_addr);

    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectStateChanged, addr, (jint) state);

    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);

    sCallbackEnv->DeleteLocalRef(addr);
}

static void get_report_callback(uint8_t type, uint8_t id, uint16_t buffer_size) {
    CHECK_CALLBACK_ENV

    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetReport, type, id, buffer_size);

    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
}

static void set_report_callback(uint8_t type, uint8_t id, uint16_t len, uint8_t *p_data) {
    jbyteArray data;

    CHECK_CALLBACK_ENV

    data = sCallbackEnv->NewByteArray(len);
    if (!data) {
        ALOGE("%s: failed to allocate storage for report data", __FUNCTION__);
        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
        return;
    }
    sCallbackEnv->SetByteArrayRegion(data, 0, len, (jbyte *) p_data);

    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSetReport, (jbyte) type, (jbyte) id, data);

    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);

    sCallbackEnv->DeleteLocalRef(data);
}

static void set_protocol_callback(uint8_t protocol) {
    CHECK_CALLBACK_ENV

    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSetProtocol, protocol);

    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
}

static void intr_data_callback(uint8_t report_id, uint16_t len, uint8_t *p_data) {
    jbyteArray data;

    CHECK_CALLBACK_ENV

    data = sCallbackEnv->NewByteArray(len);
    if (!data) {
        ALOGE("%s: failed to allocate storage for report data", __FUNCTION__);
        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
        return;
    }
    sCallbackEnv->SetByteArrayRegion(data, 0, len, (jbyte *) p_data);

    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onIntrData, (jbyte) report_id, data);

    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);

    sCallbackEnv->DeleteLocalRef(data);
}

static void vc_unplug_callback(void) {
    CHECK_CALLBACK_ENV

    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVirtualCableUnplug);

    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
}


static bthd_callbacks_t sHiddCb = {
    sizeof(sHiddCb),

    application_state_callback,
    connection_state_callback,
    get_report_callback,
    set_report_callback,
    set_protocol_callback,
    intr_data_callback,
    vc_unplug_callback,
};

static void classInitNative(JNIEnv* env, jclass clazz) {
    ALOGV("%s: done", __FUNCTION__);

    method_onApplicationStateChanged = env->GetMethodID(clazz, "onApplicationStateChanged", "([BZ)V");
    method_onConnectStateChanged = env->GetMethodID(clazz, "onConnectStateChanged", "([BI)V");
    method_onGetReport = env->GetMethodID(clazz, "onGetReport", "(BBS)V");
    method_onSetReport = env->GetMethodID(clazz, "onSetReport", "(BB[B)V");
    method_onSetProtocol = env->GetMethodID(clazz, "onSetProtocol", "(B)V");
    method_onIntrData = env->GetMethodID(clazz, "onIntrData", "(B[B)V");
    method_onVirtualCableUnplug = env->GetMethodID(clazz, "onVirtualCableUnplug", "()V");
}

static void initNative(JNIEnv *env, jobject object) {
    const bt_interface_t* btif;
    bt_status_t status;

    ALOGV("%s enter", __FUNCTION__);

    if ((btif = getBluetoothInterface()) == NULL) {
        ALOGE("Cannot obtain BT interface");
        return;
    }

    if (sHiddIf != NULL) {
        ALOGW("Cleaning up interface");
        sHiddIf->cleanup();
        sHiddIf = NULL;
    }

    if (mCallbacksObj != NULL) {
        ALOGW("Cleaning up callback object");
        env->DeleteGlobalRef(mCallbacksObj);
        mCallbacksObj = NULL;
    }

    if ((sHiddIf = (bthd_interface_t *) btif->get_profile_interface(BT_PROFILE_HIDDEV_ID))
            == NULL) {
        ALOGE("Cannot obtain interface");
        return;
    }

    if ((status = sHiddIf->init(&sHiddCb)) != BT_STATUS_SUCCESS) {
        ALOGE("Failed to initialize interface (%d)", status);
        sHiddIf = NULL;
        return;
    }

    mCallbacksObj = env->NewGlobalRef(object);

    ALOGV("%s done", __FUNCTION__);
}

static void cleanupNative(JNIEnv *env, jobject object) {
    ALOGV("%s enter", __FUNCTION__);

    if (sHiddIf !=NULL) {
        ALOGI("Cleaning up interface");
        sHiddIf->cleanup();
        sHiddIf = NULL;
    }

    if (mCallbacksObj != NULL) {
        ALOGI("Cleaning up callback object");
        env->DeleteGlobalRef(mCallbacksObj);
        mCallbacksObj = NULL;
    }

    ALOGV("%s done", __FUNCTION__);
}

static void fill_qos(JNIEnv *env, jintArray in, bthd_qos_param_t *out) {
    // set default values
    out->service_type = 0x01; // best effort
    out->token_rate = out->token_bucket_size = out->peak_bandwidth = 0; // don't care
    out->access_latency = out->delay_variation = 0xffffffff; // don't care

    if (in == NULL)
        return;

    jsize len = env->GetArrayLength(in);

    if (len != 6)
        return;

    uint32_t *buf = (uint32_t *) calloc(len, sizeof(uint32_t));

    if (buf == NULL)
        return;

    env->GetIntArrayRegion(in, 0, len, (jint *) buf);

    out->service_type = (uint8_t) buf[0];
    out->token_rate = buf[1];
    out->token_bucket_size = buf[2];
    out->peak_bandwidth = buf[3];
    out->access_latency = buf[4];
    out->delay_variation = buf[5];

    free(buf);
}

static jboolean registerAppNative(JNIEnv *env, jobject thiz, jstring name, jstring description,
        jstring provider, jbyte subclass, jbyteArray descriptors, jintArray p_in_qos, jintArray p_out_qos) {
    ALOGV("%s enter", __FUNCTION__);

    jboolean result = JNI_FALSE;
    bthd_app_param_t app_param;
    bthd_qos_param_t in_qos;
    bthd_qos_param_t out_qos;
    jsize size;
    uint8_t *data;

    size = env->GetArrayLength(descriptors);
    data = (uint8_t *) malloc(size);

    if (data != NULL) {
        env->GetByteArrayRegion(descriptors, 0, size, (jbyte *) data);

        app_param.name = env->GetStringUTFChars(name, NULL);
        app_param.description = env->GetStringUTFChars(description, NULL);
        app_param.provider = env->GetStringUTFChars(provider, NULL);
        app_param.subclass = subclass;
        app_param.desc_list = data;
        app_param.desc_list_len = size;

        fill_qos(env, p_in_qos, &in_qos);
        fill_qos(env, p_out_qos, &out_qos);

        bt_status_t ret = sHiddIf->register_app(&app_param, &in_qos, &out_qos);

        ALOGV("%s: register_app() returned %d", __FUNCTION__, ret);

        if (ret == BT_STATUS_SUCCESS)
        {
            result = JNI_TRUE;
        }

        env->ReleaseStringUTFChars(name, app_param.name);
        env->ReleaseStringUTFChars(description, app_param.description);
        env->ReleaseStringUTFChars(provider, app_param.provider);

        free(data);
    }

    ALOGV("%s done (%d)", __FUNCTION__, result);

    return result;
}

static jboolean unregisterAppNative(JNIEnv *env, jobject thiz) {
    ALOGV("%s enter", __FUNCTION__);

    jboolean result = JNI_FALSE;

    bt_status_t ret = sHiddIf->unregister_app();

    ALOGV("%s: unregister_app() returned %d", __FUNCTION__, ret);

    if (ret == BT_STATUS_SUCCESS)
    {
        result = JNI_TRUE;
    }

    ALOGV("%s done (%d)", __FUNCTION__, result);

    return result;
}

static jboolean sendReportNative(JNIEnv *env, jobject thiz, jint id, jbyteArray data) {
    ALOGV("%s enter", __FUNCTION__);

    jboolean result = JNI_FALSE;
    jsize size;
    uint8_t *buf;

    size = env->GetArrayLength(data);
    buf = (uint8_t *) malloc(size);

    if (buf != NULL) {
        env->GetByteArrayRegion(data, 0, size, (jbyte *) buf);

        bt_status_t ret = sHiddIf->send_report(BTHD_REPORT_TYPE_INTRDATA, id, size, buf);

        ALOGV("%s: send_report() returned %d", __FUNCTION__, ret);

        if (ret == BT_STATUS_SUCCESS)
        {
            result = JNI_TRUE;
        }

        free(buf);
    }

    ALOGV("%s done (%d)", __FUNCTION__, result);

    return result;
}

static jboolean replyReportNative(JNIEnv *env, jobject thiz, jbyte type, jbyte id, jbyteArray data) {
    ALOGV("%s enter", __FUNCTION__);

    jboolean result = JNI_FALSE;
    jsize size;
    uint8_t *buf;

    size = env->GetArrayLength(data);
    buf = (uint8_t *) malloc(size);

    if (buf != NULL) {
        int report_type = (type & 0x03);
        env->GetByteArrayRegion(data, 0, size, (jbyte *) buf);

        bt_status_t ret = sHiddIf->send_report((bthd_report_type_t) report_type, id, size, buf);

        ALOGV("%s: send_report() returned %d", __FUNCTION__, ret);

        if (ret == BT_STATUS_SUCCESS)
        {
            result = JNI_TRUE;
        }

        free(buf);
    }

    ALOGV("%s done (%d)", __FUNCTION__, result);

    return result;
}

static jboolean reportErrorNative(JNIEnv *env, jobject thiz) {
    ALOGV("%s enter", __FUNCTION__);

    jboolean result = JNI_FALSE;

    bt_status_t ret = sHiddIf->report_error();

    ALOGV("%s: report_error() returned %d", __FUNCTION__, ret);

    if (ret == BT_STATUS_SUCCESS)
    {
        result = JNI_TRUE;
    }

    ALOGV("%s done (%d)", __FUNCTION__, result);

    return result;
}

static jboolean unplugNative(JNIEnv *env, jobject thiz) {
    ALOGV("%s enter", __FUNCTION__);

    jboolean result = JNI_FALSE;

    bt_status_t ret = sHiddIf->virtual_cable_unplug();

    ALOGV("%s: virtual_cable_unplug() returned %d", __FUNCTION__, ret);

    if (ret == BT_STATUS_SUCCESS)
    {
        result = JNI_TRUE;
    }

    ALOGV("%s done (%d)", __FUNCTION__, result);

    return result;
}

static jboolean connectNative(JNIEnv *env, jobject thiz) {
    ALOGV("%s enter", __FUNCTION__);

    jboolean result = JNI_FALSE;

    bt_status_t ret = sHiddIf->connect();

    ALOGV("%s: connect() returned %d", __FUNCTION__, ret);

    if (ret == BT_STATUS_SUCCESS)
    {
        result = JNI_TRUE;
    }

    ALOGV("%s done (%d)", __FUNCTION__, result);

    return result;
}

static jboolean disconnectNative(JNIEnv *env, jobject thiz) {
    ALOGV("%s enter", __FUNCTION__);

    jboolean result = JNI_FALSE;

    bt_status_t ret = sHiddIf->disconnect();

    ALOGV("%s: disconnect() returned %d", __FUNCTION__, ret);

    if (ret == BT_STATUS_SUCCESS)
    {
        result = JNI_TRUE;
    }

    ALOGV("%s done (%d)", __FUNCTION__, result);

    return result;
}

static JNINativeMethod sMethods[] = {
    {"classInitNative",     "()V", (void *) classInitNative},
    {"initNative",          "()V", (void *) initNative},
    {"cleanupNative",       "()V", (void *) cleanupNative},
    {"registerAppNative",   "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;B[B[I[I)Z", (void *) registerAppNative},
    {"unregisterAppNative", "()Z", (void *) unregisterAppNative},
    {"sendReportNative",    "(I[B)Z", (void *) sendReportNative},
    {"replyReportNative",   "(BB[B)Z", (void *) replyReportNative},
    {"reportErrorNative",   "()Z", (void *) reportErrorNative},
    {"unplugNative",        "()Z", (void *) unplugNative},
    {"connectNative",       "()Z", (void *) connectNative},
    {"disconnectNative",    "()Z", (void *) disconnectNative},
};

int register_com_android_bluetooth_hidd(JNIEnv* env)
{
    return jniRegisterNativeMethods(env, "com/android/bluetooth/hid/HidDevService",
                                    sMethods, NELEM(sMethods));
}

}
Loading