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

Commit 1beed3d0 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Hearing Aid Service with native interface"

parents 2f8af3ce 40e2f4d0
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ cc_library_shared {
        "com_android_bluetooth_avrcp_controller.cpp",
        "com_android_bluetooth_hid_host.cpp",
        "com_android_bluetooth_hid_device.cpp",
        "com_android_bluetooth_hearing_aid.cpp",
        "com_android_bluetooth_hdp.cpp",
        "com_android_bluetooth_pan.cpp",
        "com_android_bluetooth_gatt.cpp",
+1 −0
Original line number Diff line number Diff line
@@ -95,6 +95,7 @@ int register_com_android_bluetooth_gatt (JNIEnv* env);

int register_com_android_bluetooth_sdp (JNIEnv* env);

int register_com_android_bluetooth_hearing_aid(JNIEnv* env);
}

#endif /* COM_ANDROID_BLUETOOTH_H */
+6 −0
Original line number Diff line number Diff line
@@ -1354,5 +1354,11 @@ jint JNI_OnLoad(JavaVM* jvm, void* reserved) {
    return JNI_ERR;
  }

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

  return JNI_VERSION_1_6;
}
+220 −0
Original line number Diff line number Diff line
/*
 * Copyright 2018 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 "BluetoothHearingAidServiceJni"

#define LOG_NDEBUG 0

#include "android_runtime/AndroidRuntime.h"
#include "base/logging.h"
#include "com_android_bluetooth.h"
#include "hardware/bt_hearing_aid.h"

#include <string.h>
#include <shared_mutex>

using bluetooth::hearing_aid::ConnectionState;
using bluetooth::hearing_aid::HearingAidInterface;
using bluetooth::hearing_aid::HearingAidCallbacks;

namespace android {
static jmethodID method_onConnectionStateChanged;
static jmethodID method_onDeviceAvailable;

static HearingAidInterface* sHearingAidInterface = nullptr;
static std::shared_timed_mutex interface_mutex;

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

class HearingAidCallbacksImpl : public HearingAidCallbacks {
 public:
  ~HearingAidCallbacksImpl() = 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 jbyteArray bd addr 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(uint8_t capabilities, uint64_t hi_sync_id,
                         const RawAddress& bd_addr) override {
    LOG(INFO) << __func__ << ": capabilities=" << +capabilities
              << " hi_sync_id=" << hi_sync_id;

    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 jbyteArray bd addr for connection state";
      return;
    }

    sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
                                     (jbyte*)&bd_addr);
    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onDeviceAvailable,
                                 (jbyte)capabilities, (jlong)hi_sync_id,
                                 addr.get());
  }
};

static HearingAidCallbacksImpl sHearingAidCallbacks;

static void classInitNative(JNIEnv* env, jclass clazz) {
  method_onConnectionStateChanged =
      env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");

  method_onDeviceAvailable =
      env->GetMethodID(clazz, "onDeviceAvailable", "(BJ[B)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 (sHearingAidInterface != nullptr) {
    LOG(INFO) << "Cleaning up HearingAid Interface before initializing...";
    sHearingAidInterface->Cleanup();
    sHearingAidInterface = 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 Aid Callbacks";
    return;
  }

  sHearingAidInterface = (HearingAidInterface*)btInf->get_profile_interface(
      BT_PROFILE_HEARING_AID_ID);
  if (sHearingAidInterface == nullptr) {
    LOG(ERROR) << "Failed to get Bluetooth Hearing Aid Interface";
    return;
  }

  sHearingAidInterface->Init(&sHearingAidCallbacks);
}

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 (sHearingAidInterface != nullptr) {
    sHearingAidInterface->Cleanup();
    sHearingAidInterface = nullptr;
  }

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

static jboolean connectHearingAidNative(JNIEnv* env, jobject object,
                                        jbyteArray address) {
  LOG(INFO) << __func__;
  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
  if (!sHearingAidInterface) return JNI_FALSE;

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

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

static jboolean disconnectHearingAidNative(JNIEnv* env, jobject object,
                                           jbyteArray address) {
  LOG(INFO) << __func__;
  std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
  if (!sHearingAidInterface) return JNI_FALSE;

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

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

static void setVolumeNative(JNIEnv* env, jclass clazz, jint volume) {
  if (!sHearingAidInterface) {
    LOG(ERROR) << __func__
               << ": Failed to get the Bluetooth Hearing Aid Interface";
    return;
  }
  sHearingAidInterface->SetVolume(volume);
}

static JNINativeMethod sMethods[] = {
    {"classInitNative", "()V", (void*)classInitNative},
    {"initNative", "()V", (void*)initNative},
    {"cleanupNative", "()V", (void*)cleanupNative},
    {"connectHearingAidNative", "([B)Z", (void*)connectHearingAidNative},
    {"disconnectHearingAidNative", "([B)Z", (void*)disconnectHearingAidNative},
    {"setVolumeNative", "(I)V", (void*)setVolumeNative},
};

int register_com_android_bluetooth_hearing_aid(JNIEnv* env) {
  return jniRegisterNativeMethods(
      env, "com/android/bluetooth/hearingaid/HearingAidNativeInterface",
      sMethods, NELEM(sMethods));
}
}  // namespace android
+10 −1
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.support.annotation.VisibleForTesting;
import android.util.Log;

import com.android.bluetooth.a2dp.A2dpService;
import com.android.bluetooth.hearingaid.HearingAidService;
import com.android.bluetooth.hfp.HeadsetService;
import com.android.bluetooth.hid.HidHostService;
import com.android.bluetooth.pan.PanService;
@@ -211,6 +212,7 @@ class PhonePolicy {
        A2dpService a2dpService = mFactory.getA2dpService();
        HeadsetService headsetService = mFactory.getHeadsetService();
        PanService panService = mFactory.getPanService();
        HearingAidService hearingAidService = mFactory.getHearingAidService();

        // Set profile priorities only for the profiles discovered on the remote device.
        // This avoids needless auto-connect attempts to profiles non-existent on the remote device
@@ -239,6 +241,13 @@ class PhonePolicy {
                .getBoolean(R.bool.config_bluetooth_pan_enable_autoconnect))) {
            panService.setPriority(device, BluetoothProfile.PRIORITY_ON);
        }

        if ((hearingAidService != null)
                && BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid)
                && (hearingAidService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED)) {
            debugLog("setting hearing aid profile priority for device " + device);
            hearingAidService.setPriority(device, BluetoothProfile.PRIORITY_ON);
        }
    }

    private void processProfileStateChanged(BluetoothDevice device, int profileId, int nextState,
@@ -463,7 +472,7 @@ class PhonePolicy {

    private static void debugLog(String msg) {
        if (DBG) {
            Log.d(TAG, msg);
            Log.i(TAG, msg);
        }
    }

Loading