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

Commit 68b17a79 authored by Jakub Tyszkowski's avatar Jakub Tyszkowski Committed by Automerger Merge Worker
Browse files

hap: Add initial implementation am: b87fc995

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Bluetooth/+/1903553

Change-Id: Ic13b45a32c30966e3fff452e100c404562836ded
parents de7f1f37 b87fc995
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -154,6 +154,8 @@ int register_com_android_bluetooth_sdp(JNIEnv* env);

int register_com_android_bluetooth_hearing_aid(JNIEnv* env);

int register_com_android_bluetooth_hap_client(JNIEnv* env);

int register_com_android_bluetooth_btservice_BluetoothKeystore(JNIEnv* env);

int register_com_android_bluetooth_btservice_activity_attribution(JNIEnv* env);
+7 −0
Original line number Diff line number Diff line
@@ -1920,6 +1920,13 @@ jint JNI_OnLoad(JavaVM* jvm, void* reserved) {
    return JNI_ERR;
  }

  status = android::register_com_android_bluetooth_hap_client(e);
  if (status < 0) {
    ALOGE("jni le audio hearing access client registration failure: %d",
          status);
    return JNI_ERR;
  }

  status = android::register_com_android_bluetooth_le_audio(e);
  if (status < 0) {
    ALOGE("jni le_audio registration failure: %d", status);
+647 −0
Original line number Diff line number Diff line
/*
 * Copyright 2021 HIMSA II K/S - www.himsa.com.
 * Represented by EHIMA - www.ehima.com
 *
 * 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 "BluetoothHapClientJni"

#define LOG_NDEBUG 0

#include <string.h>

#include <shared_mutex>

#include "base/logging.h"
#include "com_android_bluetooth.h"
#include "hardware/bt_has.h"

using bluetooth::has::ConnectionState;
using bluetooth::has::ErrorCode;
using bluetooth::has::HasClientCallbacks;
using bluetooth::has::HasClientInterface;
using bluetooth::has::PresetInfo;
using bluetooth::has::PresetInfoReason;

namespace android {
static jmethodID method_onConnectionStateChanged;
static jmethodID method_onDeviceAvailable;
static jmethodID method_onFeaturesUpdate;
static jmethodID method_onActivePresetSelected;
static jmethodID method_onGroupActivePresetSelected;
static jmethodID method_onActivePresetSelectError;
static jmethodID method_onGroupActivePresetSelectError;
static jmethodID method_onPresetInfo;
static jmethodID method_onGroupPresetInfo;
static jmethodID method_onPresetInfoError;
static jmethodID method_onGroupPresetInfoError;
static jmethodID method_onPresetNameSetError;
static jmethodID method_onGroupPresetNameSetError;

static HasClientInterface* sHasClientInterface = nullptr;
static std::shared_timed_mutex interface_mutex;

static jobject mCallbacksObj = nullptr;
static std::shared_timed_mutex callbacks_mutex;

static struct {
  jclass clazz;
  jmethodID constructor;
  jmethodID getCodecType;
  jmethodID getCodecPriority;
  jmethodID getSampleRate;
  jmethodID getBitsPerSample;
  jmethodID getChannelMode;
  jmethodID getCodecSpecific1;
  jmethodID getCodecSpecific2;
  jmethodID getCodecSpecific3;
  jmethodID getCodecSpecific4;
} android_bluetooth_BluetoothHapPresetInfo;

class HasClientCallbacksImpl : public HasClientCallbacks {
 public:
  ~HasClientCallbacksImpl() = default;

  void OnConnectionState(ConnectionState state,
                         const RawAddress& bd_addr) override {
    LOG(INFO) << __func__;

    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
    CallbackEnv sCallbackEnv(__func__);
    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;

    ScopedLocalRef<jbyteArray> addr(
        sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
    if (!addr.get()) {
      LOG(ERROR) << "Failed to new bd addr jbyteArray for connection state";
      return;
    }

    sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
                                     (jbyte*)&bd_addr);
    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
                                 (jint)state, addr.get());
  }

  void OnDeviceAvailable(const RawAddress& bd_addr, uint8_t features) override {
    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
    CallbackEnv sCallbackEnv(__func__);
    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;

    ScopedLocalRef<jbyteArray> addr(
        sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
    if (!addr.get()) {
      LOG(ERROR) << "Failed to new bd addr jbyteArray for device available";
      return;
    }
    sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
                                     (jbyte*)&bd_addr);

    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onDeviceAvailable,
                                 addr.get(), (jint)features);
  }

  void OnFeaturesUpdate(const RawAddress& bd_addr, uint8_t features) override {
    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
    CallbackEnv sCallbackEnv(__func__);
    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;

    ScopedLocalRef<jbyteArray> addr(
        sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
    if (!addr.get()) {
      LOG(ERROR) << "Failed to new bd addr jbyteArray for device available";
      return;
    }
    sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
                                     (jbyte*)&bd_addr);

    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onFeaturesUpdate,
                                 addr.get(), (jint)features);
  }

  void OnActivePresetSelected(std::variant<RawAddress, int> addr_or_group_id,
                              uint8_t preset_index) override {
    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
    CallbackEnv sCallbackEnv(__func__);
    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;

    if (std::holds_alternative<RawAddress>(addr_or_group_id)) {
      ScopedLocalRef<jbyteArray> addr(
          sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
      if (!addr.get()) {
        LOG(ERROR) << "Failed to new bd addr jbyteArray for preset selected";
        return;
      }
      sCallbackEnv->SetByteArrayRegion(
          addr.get(), 0, sizeof(RawAddress),
          (jbyte*)&std::get<RawAddress>(addr_or_group_id));

      sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onActivePresetSelected,
                                   addr.get(), (jint)preset_index);
    } else {
      sCallbackEnv->CallVoidMethod(
          mCallbacksObj, method_onGroupActivePresetSelected,
          std::get<int>(addr_or_group_id), (jint)preset_index);
    }
  }

  void OnActivePresetSelectError(std::variant<RawAddress, int> addr_or_group_id,
                                 ErrorCode error_code) override {
    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
    CallbackEnv sCallbackEnv(__func__);
    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;

    if (std::holds_alternative<RawAddress>(addr_or_group_id)) {
      ScopedLocalRef<jbyteArray> addr(
          sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
      if (!addr.get()) {
        LOG(ERROR)
            << "Failed to new bd addr jbyteArray for preset select error";
        return;
      }
      sCallbackEnv->SetByteArrayRegion(
          addr.get(), 0, sizeof(RawAddress),
          (jbyte*)&std::get<RawAddress>(addr_or_group_id));

      sCallbackEnv->CallVoidMethod(mCallbacksObj,
                                   method_onActivePresetSelectError, addr.get(),
                                   (jint)error_code);
    } else {
      sCallbackEnv->CallVoidMethod(
          mCallbacksObj, method_onGroupActivePresetSelectError,
          std::get<int>(addr_or_group_id), (jint)error_code);
    }
  }

  void OnPresetInfo(std::variant<RawAddress, int> addr_or_group_id,
                    PresetInfoReason info_reason,
                    std::vector<PresetInfo> detail_records) override {
    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
    CallbackEnv sCallbackEnv(__func__);
    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;

    jsize i = 0;
    jobjectArray presets_array = sCallbackEnv->NewObjectArray(
        (jsize)detail_records.size(),
        android_bluetooth_BluetoothHapPresetInfo.clazz, nullptr);

    const char null_str[] = "";
    for (auto const& info : detail_records) {
      const char* name = info.preset_name.c_str();
      if (!sCallbackEnv.isValidUtf(name)) {
        ALOGE("%s: name is not a valid UTF string.", __func__);
        name = null_str;
      }

      ScopedLocalRef<jstring> name_str(sCallbackEnv.get(),
                                       sCallbackEnv->NewStringUTF(name));
      if (!name_str.get()) {
        LOG(ERROR) << "Failed to new preset name String for preset name";
        return;
      }

      jobject infoObj = sCallbackEnv->NewObject(
          android_bluetooth_BluetoothHapPresetInfo.clazz,
          android_bluetooth_BluetoothHapPresetInfo.constructor,
          (jint)info.preset_index, name_str.get(), (jboolean)info.writable,
          (jboolean)info.available);
      sCallbackEnv->SetObjectArrayElement(presets_array, i++, infoObj);
      sCallbackEnv->DeleteLocalRef(infoObj);
    }

    if (std::holds_alternative<RawAddress>(addr_or_group_id)) {
      ScopedLocalRef<jbyteArray> addr(
          sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
      if (!addr.get()) {
        LOG(ERROR) << "Failed to new bd addr jbyteArray for preset name";
        return;
      }
      sCallbackEnv->SetByteArrayRegion(
          addr.get(), 0, sizeof(RawAddress),
          (jbyte*)&std::get<RawAddress>(addr_or_group_id));

      sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onPresetInfo,
                                   addr.get(), (jint)info_reason,
                                   presets_array);
    } else {
      sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGroupPresetInfo,
                                   std::get<int>(addr_or_group_id),
                                   (jint)info_reason, presets_array);
    }
  }

  virtual void OnPresetInfoError(std::variant<RawAddress, int> addr_or_group_id,
                                 uint8_t preset_index,
                                 ErrorCode error_code) override {
    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
    CallbackEnv sCallbackEnv(__func__);
    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;

    if (std::holds_alternative<RawAddress>(addr_or_group_id)) {
      ScopedLocalRef<jbyteArray> addr(
          sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
      if (!addr.get()) {
        LOG(ERROR)
            << "Failed to new bd addr jbyteArray for preset name get error";
        return;
      }
      sCallbackEnv->SetByteArrayRegion(
          addr.get(), 0, sizeof(RawAddress),
          (jbyte*)&std::get<RawAddress>(addr_or_group_id));

      sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onPresetInfoError,
                                   addr.get(), (jint)preset_index,
                                   (jint)error_code);
    } else {
      sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGroupPresetInfoError,
                                   std::get<int>(addr_or_group_id),
                                   (jint)preset_index, (jint)error_code);
    }
  }

  void OnSetPresetNameError(std::variant<RawAddress, int> addr_or_group_id,
                            uint8_t preset_index,
                            ErrorCode error_code) override {
    std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
    CallbackEnv sCallbackEnv(__func__);
    if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;

    if (std::holds_alternative<RawAddress>(addr_or_group_id)) {
      ScopedLocalRef<jbyteArray> addr(
          sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
      if (!addr.get()) {
        LOG(ERROR)
            << "Failed to new bd addr jbyteArray for preset name set error";
        return;
      }
      sCallbackEnv->SetByteArrayRegion(
          addr.get(), 0, sizeof(RawAddress),
          (jbyte*)&std::get<RawAddress>(addr_or_group_id));

      sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onPresetNameSetError,
                                   addr.get(), (jint)preset_index,
                                   (jint)error_code);
    } else {
      sCallbackEnv->CallVoidMethod(mCallbacksObj,
                                   method_onGroupPresetNameSetError,
                                   std::get<int>(addr_or_group_id),
                                   (jint)preset_index, (jint)error_code);
    }
  }
};

static HasClientCallbacksImpl sHasClientCallbacks;

static void classInitNative(JNIEnv* env, jclass clazz) {
  jclass jniBluetoothBluetoothHapPresetInfoClass =
      env->FindClass("android/bluetooth/BluetoothHapPresetInfo");
  CHECK(jniBluetoothBluetoothHapPresetInfoClass != NULL);

  android_bluetooth_BluetoothHapPresetInfo.constructor =
      env->GetMethodID(jniBluetoothBluetoothHapPresetInfoClass, "<init>",
                       "(ILjava/lang/String;ZZ)V");

  method_onConnectionStateChanged =
      env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");

  method_onDeviceAvailable =
      env->GetMethodID(clazz, "onDeviceAvailable", "([BI)V");

  method_onFeaturesUpdate =
      env->GetMethodID(clazz, "onFeaturesUpdate", "([BI)V");

  method_onActivePresetSelected =
      env->GetMethodID(clazz, "onActivePresetSelected", "([BI)V");

  method_onGroupActivePresetSelected =
      env->GetMethodID(clazz, "onActivePresetGroupSelected", "(II)V");

  method_onActivePresetSelectError =
      env->GetMethodID(clazz, "onActivePresetSelectError", "([BI)V");

  method_onGroupActivePresetSelectError =
      env->GetMethodID(clazz, "onActivePresetGroupSelectError", "(II)V");

  method_onPresetInfo =
      env->GetMethodID(clazz, "onPresetInfo",
                       "([BI[Landroid/bluetooth/BluetoothHapPresetInfo;)V");

  method_onGroupPresetInfo =
      env->GetMethodID(clazz, "onGroupPresetInfo",
                       "(II[Landroid/bluetooth/BluetoothHapPresetInfo;)V");

  method_onPresetNameSetError =
      env->GetMethodID(clazz, "onPresetNameSetError", "([BII)V");

  method_onGroupPresetNameSetError =
      env->GetMethodID(clazz, "onGroupPresetNameSetError", "(III)V");

  method_onPresetInfoError =
      env->GetMethodID(clazz, "onPresetInfoError", "([BII)V");

  method_onGroupPresetInfoError =
      env->GetMethodID(clazz, "onGroupPresetInfoError", "(III)V");

  LOG(INFO) << __func__ << ": succeeds";
}

static void initNative(JNIEnv* env, jobject object) {
  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
  std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);

  const bt_interface_t* btInf = getBluetoothInterface();
  if (btInf == nullptr) {
    LOG(ERROR) << "Bluetooth module is not loaded";
    return;
  }

  if (sHasClientInterface != nullptr) {
    LOG(INFO) << "Cleaning up HearingAid Interface before initializing...";
    sHasClientInterface->Cleanup();
    sHasClientInterface = nullptr;
  }

  if (mCallbacksObj != nullptr) {
    LOG(INFO) << "Cleaning up HearingAid callback object";
    env->DeleteGlobalRef(mCallbacksObj);
    mCallbacksObj = nullptr;
  }

  if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {
    LOG(ERROR) << "Failed to allocate Global Ref for Hearing Access Callbacks";
    return;
  }

  android_bluetooth_BluetoothHapPresetInfo.clazz = (jclass)env->NewGlobalRef(
      env->FindClass("android/bluetooth/BluetoothHapPresetInfo"));
  if (android_bluetooth_BluetoothHapPresetInfo.clazz == nullptr) {
    ALOGE("%s: Failed to allocate Global Ref for BluetoothHapPresetInfo class",
          __func__);
    return;
  }

  sHasClientInterface = (HasClientInterface*)btInf->get_profile_interface(
      BT_PROFILE_HAP_CLIENT_ID);
  if (sHasClientInterface == nullptr) {
    LOG(ERROR)
        << "Failed to get Bluetooth Hearing Access Service Client Interface";
    return;
  }

  sHasClientInterface->Init(&sHasClientCallbacks);
}

static void cleanupNative(JNIEnv* env, jobject object) {
  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
  std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);

  const bt_interface_t* btInf = getBluetoothInterface();
  if (btInf == nullptr) {
    LOG(ERROR) << "Bluetooth module is not loaded";
    return;
  }

  if (sHasClientInterface != nullptr) {
    sHasClientInterface->Cleanup();
    sHasClientInterface = nullptr;
  }

  if (mCallbacksObj != nullptr) {
    env->DeleteGlobalRef(mCallbacksObj);
    mCallbacksObj = nullptr;
  }
}

static jboolean connectHapClientNative(JNIEnv* env, jobject object,
                                       jbyteArray address) {
  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
  if (!sHasClientInterface) {
    LOG(ERROR) << __func__ << ": Failed to get the Bluetooth HAP Interface";
    return JNI_FALSE;
  }

  jbyte* addr = env->GetByteArrayElements(address, nullptr);
  if (!addr) {
    jniThrowIOException(env, EINVAL);
    return JNI_FALSE;
  }

  RawAddress* tmpraw = (RawAddress*)addr;
  sHasClientInterface->Connect(*tmpraw);
  env->ReleaseByteArrayElements(address, addr, 0);
  return JNI_TRUE;
}

static jboolean disconnectHapClientNative(JNIEnv* env, jobject object,
                                          jbyteArray address) {
  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
  if (!sHasClientInterface) {
    LOG(ERROR) << __func__ << ": Failed to get the Bluetooth HAP Interface";
    return JNI_FALSE;
  }

  jbyte* addr = env->GetByteArrayElements(address, nullptr);
  if (!addr) {
    jniThrowIOException(env, EINVAL);
    return JNI_FALSE;
  }

  RawAddress* tmpraw = (RawAddress*)addr;
  sHasClientInterface->Disconnect(*tmpraw);
  env->ReleaseByteArrayElements(address, addr, 0);
  return JNI_TRUE;
}

static void selectActivePresetNative(JNIEnv* env, jobject object,
                                     jbyteArray address, jint preset_index) {
  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
  if (!sHasClientInterface) {
    LOG(ERROR) << __func__ << ": Failed to get the Bluetooth HAP Interface";
    return;
  }

  jbyte* addr = env->GetByteArrayElements(address, nullptr);
  if (!addr) {
    jniThrowIOException(env, EINVAL);
    return;
  }

  RawAddress* tmpraw = (RawAddress*)addr;
  sHasClientInterface->SelectActivePreset(*tmpraw, preset_index);
  env->ReleaseByteArrayElements(address, addr, 0);
}

static void groupSelectActivePresetNative(JNIEnv* env, jobject object,
                                          jint group_id, jint preset_index) {
  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
  if (!sHasClientInterface) {
    LOG(ERROR) << __func__ << ": Failed to get the Bluetooth HAP Interface";
    return;
  }

  sHasClientInterface->SelectActivePreset(group_id, preset_index);
}

static void nextActivePresetNative(JNIEnv* env, jobject object,
                                   jbyteArray address) {
  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
  if (!sHasClientInterface) {
    LOG(ERROR) << __func__ << ": Failed to get the Bluetooth HAP Interface";
    return;
  }

  jbyte* addr = env->GetByteArrayElements(address, nullptr);
  if (!addr) {
    jniThrowIOException(env, EINVAL);
    return;
  }

  RawAddress* tmpraw = (RawAddress*)addr;
  sHasClientInterface->NextActivePreset(*tmpraw);
  env->ReleaseByteArrayElements(address, addr, 0);
}

static void groupNextActivePresetNative(JNIEnv* env, jobject object,
                                        jint group_id) {
  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
  if (!sHasClientInterface) {
    LOG(ERROR) << __func__ << ": Failed to get the Bluetooth HAP Interface";
    return;
  }

  sHasClientInterface->NextActivePreset(group_id);
}

static void previousActivePresetNative(JNIEnv* env, jobject object,
                                       jbyteArray address) {
  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
  if (!sHasClientInterface) {
    LOG(ERROR) << __func__ << ": Failed to get the Bluetooth HAP Interface";
    return;
  }

  jbyte* addr = env->GetByteArrayElements(address, nullptr);
  if (!addr) {
    jniThrowIOException(env, EINVAL);
    return;
  }

  RawAddress* tmpraw = (RawAddress*)addr;
  sHasClientInterface->PreviousActivePreset(*tmpraw);
  env->ReleaseByteArrayElements(address, addr, 0);
}

static void groupPreviousActivePresetNative(JNIEnv* env, jobject object,
                                            jint group_id) {
  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
  if (!sHasClientInterface) {
    LOG(ERROR) << __func__ << ": Failed to get the Bluetooth HAP Interface";
    return;
  }

  sHasClientInterface->PreviousActivePreset(group_id);
}

static void getPresetInfoNative(JNIEnv* env, jobject object, jbyteArray address,
                                jint preset_index) {
  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
  if (!sHasClientInterface) {
    LOG(ERROR) << __func__ << ": Failed to get the Bluetooth HAP Interface";
    return;
  }

  jbyte* addr = env->GetByteArrayElements(address, nullptr);
  if (!addr) {
    jniThrowIOException(env, EINVAL);
    return;
  }

  RawAddress* tmpraw = (RawAddress*)addr;
  sHasClientInterface->GetPresetInfo(*tmpraw, preset_index);
  env->ReleaseByteArrayElements(address, addr, 0);
}

static void setPresetNameNative(JNIEnv* env, jobject object, jbyteArray address,
                                jint preset_index, jstring name) {
  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
  if (!sHasClientInterface) {
    LOG(ERROR) << __func__ << ": Failed to get the Bluetooth HAP Interface";
    return;
  }

  jbyte* addr = env->GetByteArrayElements(address, nullptr);
  if (!addr) {
    jniThrowIOException(env, EINVAL);
    return;
  }

  std::string name_str;
  if (name != nullptr) {
    const char* value = env->GetStringUTFChars(name, nullptr);
    name_str = std::string(value);
    env->ReleaseStringUTFChars(name, value);
  }

  RawAddress* tmpraw = (RawAddress*)addr;
  sHasClientInterface->SetPresetName(*tmpraw, preset_index,
                                     std::move(name_str));
  env->ReleaseByteArrayElements(address, addr, 0);
}

static void groupSetPresetNameNative(JNIEnv* env, jobject object, jint group_id,
                                     jint preset_index, jstring name) {
  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
  if (!sHasClientInterface) {
    LOG(ERROR) << __func__ << ": Failed to get the Bluetooth HAP Interface";
    return;
  }

  std::string name_str;
  if (name != nullptr) {
    const char* value = env->GetStringUTFChars(name, nullptr);
    name_str = std::string(value);
    env->ReleaseStringUTFChars(name, value);
  }

  sHasClientInterface->SetPresetName(group_id, preset_index,
                                     std::move(name_str));
}

static JNINativeMethod sMethods[] = {
    {"classInitNative", "()V", (void*)classInitNative},
    {"initNative", "()V", (void*)initNative},
    {"cleanupNative", "()V", (void*)cleanupNative},
    {"connectHapClientNative", "([B)Z", (void*)connectHapClientNative},
    {"disconnectHapClientNative", "([B)Z", (void*)disconnectHapClientNative},
    {"selectActivePresetNative", "([BI)V", (void*)selectActivePresetNative},
    {"groupSelectActivePresetNative", "(II)V",
     (void*)groupSelectActivePresetNative},
    {"nextActivePresetNative", "([B)V", (void*)nextActivePresetNative},
    {"groupNextActivePresetNative", "(I)V", (void*)groupNextActivePresetNative},
    {"previousActivePresetNative", "([B)V", (void*)previousActivePresetNative},
    {"groupPreviousActivePresetNative", "(I)V",
     (void*)groupPreviousActivePresetNative},
    {"getPresetInfoNative", "([BI)V", (void*)getPresetInfoNative},
    {"setPresetNameNative", "([BILjava/lang/String;)V",
     (void*)setPresetNameNative},
    {"groupSetPresetNameNative", "(IILjava/lang/String;)V",
     (void*)groupSetPresetNameNative},
};

int register_com_android_bluetooth_hap_client(JNIEnv* env) {
  return jniRegisterNativeMethods(
      env, "com/android/bluetooth/hap/HapClientNativeInterface", sMethods,
      NELEM(sMethods));
}
}  // namespace android
Loading