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

Commit 3bbfe7aa authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "leaudio: Add BAP Broadcaster implementation"

parents 920c847f 8604826c
Loading
Loading
Loading
Loading
+266 −2
Original line number Diff line number Diff line
@@ -27,11 +27,16 @@
#include "com_android_bluetooth.h"
#include "hardware/bt_le_audio.h"

using bluetooth::le_audio::BroadcastAudioProfile;
using bluetooth::le_audio::BroadcastId;
using bluetooth::le_audio::BroadcastState;
using bluetooth::le_audio::btle_audio_codec_config_t;
using bluetooth::le_audio::btle_audio_codec_index_t;
using bluetooth::le_audio::ConnectionState;
using bluetooth::le_audio::GroupNodeStatus;
using bluetooth::le_audio::GroupStatus;
using bluetooth::le_audio::LeAudioBroadcasterCallbacks;
using bluetooth::le_audio::LeAudioBroadcasterInterface;
using bluetooth::le_audio::LeAudioClientCallbacks;
using bluetooth::le_audio::LeAudioClientInterface;

@@ -299,7 +304,6 @@ static jboolean groupAddNodeNative(JNIEnv* env, jobject object, jint group_id,

static jboolean groupRemoveNodeNative(JNIEnv* env, jobject object,
                                      jint group_id, jbyteArray address) {

  if (!sLeAudioClientInterface) {
    LOG(ERROR) << __func__ << ": Failed to get the Bluetooth LeAudio Interface";
    return JNI_FALSE;
@@ -340,9 +344,269 @@ static JNINativeMethod sMethods[] = {
    {"groupSetActiveNative", "(I)V", (void*)groupSetActiveNative},
};

/* Le Audio Broadcaster */
static jmethodID method_onBroadcastCreated;
static jmethodID method_onBroadcastDestroyed;
static jmethodID method_onBroadcastStateChanged;
static jmethodID method_onBroadcastId;

static LeAudioBroadcasterInterface* sLeAudioBroadcasterInterface = nullptr;
static std::shared_timed_mutex sBroadcasterInterfaceMutex;

static jobject sBroadcasterCallbacksObj = nullptr;
static std::shared_timed_mutex sBroadcasterCallbacksMutex;

class LeAudioBroadcasterCallbacksImpl : public LeAudioBroadcasterCallbacks {
 public:
  ~LeAudioBroadcasterCallbacksImpl() = default;

  void OnBroadcastCreated(uint8_t instance_id, bool success) override {
    LOG(INFO) << __func__;

    std::shared_lock<std::shared_timed_mutex> lock(sBroadcasterCallbacksMutex);
    CallbackEnv sCallbackEnv(__func__);

    if (!sCallbackEnv.valid() || sBroadcasterCallbacksObj == nullptr) return;
    sCallbackEnv->CallVoidMethod(sBroadcasterCallbacksObj,
                                 method_onBroadcastCreated, (jint)instance_id,
                                 success ? JNI_TRUE : JNI_FALSE);
  }

  void OnBroadcastDestroyed(uint8_t instance_id) override {
    LOG(INFO) << __func__;

    std::shared_lock<std::shared_timed_mutex> lock(sBroadcasterCallbacksMutex);
    CallbackEnv sCallbackEnv(__func__);

    if (!sCallbackEnv.valid() || sBroadcasterCallbacksObj == nullptr) return;
    sCallbackEnv->CallVoidMethod(sBroadcasterCallbacksObj,
                                 method_onBroadcastDestroyed,
                                 (jint)instance_id);
  }

  void OnBroadcastStateChanged(uint8_t instance_id,
                               BroadcastState state) override {
    LOG(INFO) << __func__;

    std::shared_lock<std::shared_timed_mutex> lock(sBroadcasterCallbacksMutex);
    CallbackEnv sCallbackEnv(__func__);

    if (!sCallbackEnv.valid() || sBroadcasterCallbacksObj == nullptr) return;
    sCallbackEnv->CallVoidMethod(
        sBroadcasterCallbacksObj, method_onBroadcastStateChanged,
        (jint)instance_id,
        (jint) static_cast<std::underlying_type<BroadcastState>::type>(state));
  }

  void OnBroadcastId(uint8_t instance_id,
                     const BroadcastId& broadcast_id) override {
    LOG(INFO) << __func__;

    std::shared_lock<std::shared_timed_mutex> lock(sBroadcasterCallbacksMutex);
    CallbackEnv sCallbackEnv(__func__);

    // broadcast_id
    int field_size = broadcast_id.size();
    ScopedLocalRef<jbyteArray> serialized_broadcast_id(
        sCallbackEnv.get(), sCallbackEnv->NewByteArray(field_size));
    if (!serialized_broadcast_id.get()) {
      LOG(ERROR) << "Failed to allocate new jbyteArray broadcast_id for the "
                    "announcement";
      return;
    }

    sCallbackEnv->SetByteArrayRegion(serialized_broadcast_id.get(), 0,
                                     field_size, (jbyte*)broadcast_id.data());

    if (!sCallbackEnv.valid() || sBroadcasterCallbacksObj == nullptr) return;
    sCallbackEnv->CallVoidMethod(sBroadcasterCallbacksObj, method_onBroadcastId,
                                 (jint)instance_id,
                                 serialized_broadcast_id.get());
  }
};

static LeAudioBroadcasterCallbacksImpl sLeAudioBroadcasterCallbacks;

static void BroadcasterClassInitNative(JNIEnv* env, jclass clazz) {
  method_onBroadcastCreated =
      env->GetMethodID(clazz, "onBroadcastCreated", "(IZ)V");
  method_onBroadcastDestroyed =
      env->GetMethodID(clazz, "onBroadcastDestroyed", "(I)V");
  method_onBroadcastStateChanged =
      env->GetMethodID(clazz, "onBroadcastStateChanged", "(II)V");
  method_onBroadcastId = env->GetMethodID(clazz, "onBroadcastId", "(I[B)V");
}

static void BroadcasterInitNative(JNIEnv* env, jobject object) {
  std::unique_lock<std::shared_timed_mutex> interface_lock(
      sBroadcasterInterfaceMutex);
  std::unique_lock<std::shared_timed_mutex> callbacks_lock(
      sBroadcasterCallbacksMutex);

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

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

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

  sLeAudioBroadcasterInterface =
      (LeAudioBroadcasterInterface*)btInf->get_profile_interface(
          BT_PROFILE_LE_AUDIO_BROADCASTER_ID);
  if (sLeAudioBroadcasterInterface == nullptr) {
    LOG(ERROR) << "Failed to get Bluetooth LeAudio Broadcaster Interface";
    return;
  }

  sLeAudioBroadcasterInterface->Initialize(&sLeAudioBroadcasterCallbacks);
}

static void BroadcasterStopNative(JNIEnv* env, jobject object) {
  std::unique_lock<std::shared_timed_mutex> interface_lock(
      sBroadcasterInterfaceMutex);

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

  if (sLeAudioBroadcasterInterface != nullptr)
    sLeAudioBroadcasterInterface->Stop();
}

static void BroadcasterCleanupNative(JNIEnv* env, jobject object) {
  std::unique_lock<std::shared_timed_mutex> interface_lock(
      sBroadcasterInterfaceMutex);
  std::unique_lock<std::shared_timed_mutex> callbacks_lock(
      sBroadcasterCallbacksMutex);

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

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

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

static void CreateBroadcastNative(JNIEnv* env, jobject object,
                                  jbyteArray metadata, jint audio_profile,
                                  jbyteArray broadcast_code) {
  LOG(INFO) << __func__;
  std::shared_lock<std::shared_timed_mutex> lock(sBroadcasterInterfaceMutex);
  if (!sLeAudioBroadcasterInterface) return;

  std::array<uint8_t, 16> code_array;
  if (broadcast_code)
    env->GetByteArrayRegion(broadcast_code, 0, 16, (jbyte*)code_array.data());

  jbyte* meta = env->GetByteArrayElements(metadata, nullptr);
  sLeAudioBroadcasterInterface->CreateBroadcast(
      std::vector<uint8_t>(meta, meta + env->GetArrayLength(metadata)),
      static_cast<BroadcastAudioProfile>(audio_profile),
      broadcast_code ? std::optional<std::array<uint8_t, 16>>(code_array)
                     : std::nullopt);
  env->ReleaseByteArrayElements(metadata, meta, 0);
}

static void UpdateMetadataNative(JNIEnv* env, jobject object, jint instance_id,
                                 jbyteArray metadata) {
  jbyte* meta = env->GetByteArrayElements(metadata, nullptr);
  sLeAudioBroadcasterInterface->UpdateMetadata(
      instance_id,
      std::vector<uint8_t>(meta, meta + env->GetArrayLength(metadata)));
  env->ReleaseByteArrayElements(metadata, meta, 0);
}

static void StartBroadcastNative(JNIEnv* env, jobject object,
                                 jint instance_id) {
  LOG(INFO) << __func__;
  std::shared_lock<std::shared_timed_mutex> lock(sBroadcasterInterfaceMutex);
  if (!sLeAudioBroadcasterInterface) return;
  sLeAudioBroadcasterInterface->StartBroadcast(instance_id);
}

static void StopBroadcastNative(JNIEnv* env, jobject object, jint instance_id) {
  LOG(INFO) << __func__;
  std::shared_lock<std::shared_timed_mutex> lock(sBroadcasterInterfaceMutex);
  if (!sLeAudioBroadcasterInterface) return;
  sLeAudioBroadcasterInterface->StopBroadcast(instance_id);
}

static void PauseBroadcastNative(JNIEnv* env, jobject object,
                                 jint instance_id) {
  LOG(INFO) << __func__;
  std::shared_lock<std::shared_timed_mutex> lock(sBroadcasterInterfaceMutex);
  if (!sLeAudioBroadcasterInterface) return;
  sLeAudioBroadcasterInterface->PauseBroadcast(instance_id);
}

static void DestroyBroadcastNative(JNIEnv* env, jobject object,
                                   jint instance_id) {
  LOG(INFO) << __func__;
  std::shared_lock<std::shared_timed_mutex> lock(sBroadcasterInterfaceMutex);
  if (!sLeAudioBroadcasterInterface) return;
  sLeAudioBroadcasterInterface->DestroyBroadcast(instance_id);
}

static void GetBroadcastIdNative(JNIEnv* env, jobject object,
                                 jint instance_id) {
  LOG(INFO) << __func__;
  std::shared_lock<std::shared_timed_mutex> lock(sBroadcasterInterfaceMutex);
  if (!sLeAudioBroadcasterInterface) return;
  sLeAudioBroadcasterInterface->GetBroadcastId(instance_id);
}

static void GetAllBroadcastStatesNative(JNIEnv* env, jobject object) {
  LOG(INFO) << __func__;
  std::shared_lock<std::shared_timed_mutex> lock(sBroadcasterInterfaceMutex);
  if (!sLeAudioBroadcasterInterface) return;
  sLeAudioBroadcasterInterface->GetAllBroadcastStates();
}

static JNINativeMethod sBroadcasterMethods[] = {
    {"classInitNative", "()V", (void*)BroadcasterClassInitNative},
    {"initNative", "()V", (void*)BroadcasterInitNative},
    {"stopNative", "()V", (void*)BroadcasterStopNative},
    {"cleanupNative", "()V", (void*)BroadcasterCleanupNative},
    {"createBroadcastNative", "([BI[B)V", (void*)CreateBroadcastNative},
    {"updateMetadataNative", "(I[B)V", (void*)UpdateMetadataNative},
    {"startBroadcastNative", "(I)V", (void*)StartBroadcastNative},
    {"stopBroadcastNative", "(I)V", (void*)StopBroadcastNative},
    {"pauseBroadcastNative", "(I)V", (void*)PauseBroadcastNative},
    {"destroyBroadcastNative", "(I)V", (void*)DestroyBroadcastNative},
    {"getBroadcastIdNative", "(I)V", (void*)GetBroadcastIdNative},
    {"getAllBroadcastStatesNative", "()V", (void*)GetAllBroadcastStatesNative},
};

int register_com_android_bluetooth_le_audio(JNIEnv* env) {
  return jniRegisterNativeMethods(
  int register_success = jniRegisterNativeMethods(
      env, "com/android/bluetooth/le_audio/LeAudioNativeInterface", sMethods,
      NELEM(sMethods));
  return register_success &
         jniRegisterNativeMethods(
             env,
             "com/android/bluetooth/le_audio/LeAudioBroadcasterNativeInterface",
             sBroadcasterMethods, NELEM(sBroadcasterMethods));
}
}  // namespace android
+260 −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.
 */

/*
 * Defines the native interface that is used by state machine/service to
 * send or receive messages from the native stack. This file is registered
 * for the native methods in the corresponding JNI C++ file.
 */
package com.android.bluetooth.le_audio;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.util.Log;

import com.android.bluetooth.Utils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;

/**
 * LeAudio Native Interface to/from JNI.
 */
public class LeAudioBroadcasterNativeInterface {
    private static final String TAG = "LeAudioBroadcasterNativeInterface";
    private static final boolean DBG = true;
    private BluetoothAdapter mAdapter;

    @GuardedBy("INSTANCE_LOCK")
    private static LeAudioBroadcasterNativeInterface sInstance;
    private static final Object INSTANCE_LOCK = new Object();

    static {
        classInitNative();
    }

    private LeAudioBroadcasterNativeInterface() {
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        if (mAdapter == null) {
            Log.wtfStack(TAG, "No Bluetooth Adapter Available");
        }
    }

    /**
     * Get singleton instance.
     */
    public static LeAudioBroadcasterNativeInterface getInstance() {
        synchronized (INSTANCE_LOCK) {
            if (sInstance == null) {
                sInstance = new LeAudioBroadcasterNativeInterface();
            }
            return sInstance;
        }
    }

    private void sendMessageToService(LeAudioStackEvent event) {
        LeAudioService service = LeAudioService.getLeAudioService();
        if (service != null) {
            service.messageFromNative(event);
        } else {
            Log.e(TAG, "Event ignored, service not available: " + event);
        }
    }

    @VisibleForTesting
    public BluetoothDevice getDevice(byte[] address) {
        return mAdapter.getRemoteDevice(address);
    }

    // Callbacks from the native stack back into the Java framework.
    @VisibleForTesting
    public void onBroadcastCreated(int instanceId, boolean success) {
        if (DBG) {
            Log.d(TAG, "onBroadcastCreated: instanceId=" + instanceId);
        }
        LeAudioStackEvent event =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED);

        event.valueInt1 = instanceId;
        event.valueBool1 = success;
        sendMessageToService(event);
    }

    @VisibleForTesting
    public void onBroadcastDestroyed(int instanceId) {
        if (DBG) {
            Log.d(TAG, "onBroadcastDestroyed: instanceId=" + instanceId);
        }
        LeAudioStackEvent event =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_DESTROYED);

        event.valueInt1 = instanceId;
        sendMessageToService(event);
    }

    @VisibleForTesting
    public void onBroadcastStateChanged(int instanceId, int state) {
        if (DBG) {
            Log.d(TAG, "onBroadcastStateChanged: instanceId=" + instanceId + " state=" + state);
        }
        LeAudioStackEvent event =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_STATE);

        /* NOTICE: This is a fake device to satisfy Audio Manager in the upper
         * layers which needs a device instance to route audio streams to the
         * proper module (here it's Bluetooth). Broadcast has no concept of a
         * destination or peer device therefore this fake device was created.
         * For now it's only important that this device is a Bluetooth device.
         */
        event.device = getDevice(Utils.getBytesFromAddress("FF:FF:FF:FF:FF:FF"));
        event.valueInt1 = instanceId;
        event.valueInt2 = state;
        sendMessageToService(event);
    }

    @VisibleForTesting
    public void onBroadcastId(int instanceId, byte[] broadcastId) {
        LeAudioStackEvent event =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_ID);
        event.valueInt1 = instanceId;
        event.valueByte1 = broadcastId;
        if (DBG) {
            Log.d(TAG, "onBroadcastId: " + event);
        }
        sendMessageToService(event);
    }

    /**
     * Initializes the native interface.
     *
     * priorities to configure.
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public void init() {
        initNative();
    }

    /**
     * Stop the Broadcast Service.
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public void stop() {
        stopNative();
    }

    /**
     * Cleanup the native interface.
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public void cleanup() {
        cleanupNative();
    }

    /**
     * Creates LeAudio Broadcast instance.
     *
     * @param metadata metadata buffer with TLVs
     * @param audioProfile broadcast audio profile
     * @param broadcastCode optional code if broadcast should be encrypted
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public void createBroadcast(byte[] metadata, int audioProfile, byte[] broadcastCode) {
        createBroadcastNative(metadata, audioProfile, broadcastCode);
    }

    /**
     * Update LeAudio Broadcast instance metadata.
     *
     * @param instanceId broadcast instance identifier
     * @param metadata metadata buffer with TLVs
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public void updateMetadata(int instanceId, byte[] metadata) {
        updateMetadataNative(instanceId, metadata);
    }

    /**
     * Start LeAudio Broadcast instance.
     *
     * @param instanceId broadcast instance identifier
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public void startBroadcast(int instanceId) {
        startBroadcastNative(instanceId);
    }

    /**
     * Stop LeAudio Broadcast instance.
     *
     * @param instanceId broadcast instance identifier
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public void stopBroadcast(int instanceId) {
        stopBroadcastNative(instanceId);
    }

    /**
     * Pause LeAudio Broadcast instance.
     *
     * @param instanceId broadcast instance identifier
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public void pauseBroadcast(int instanceId) {
        pauseBroadcastNative(instanceId);
    }

    /**
     * Destroy LeAudio Broadcast instance.
     *
     * @param instanceId broadcast instance identifier
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public void destroyBroadcast(int instanceId) {
        destroyBroadcastNative(instanceId);
    }

    /**
     * Get LeAudio Broadcast instance advertising address.
     *
     * @param instanceId broadcast instance identifier
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public void getBroadcastId(int instanceId) {
        getBroadcastIdNative(instanceId);
    }

    /**
     * Get all LeAudio Broadcast instance states.
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public void getAllBroadcastStates() {
        getAllBroadcastStatesNative();
    }

    // Native methods that call into the JNI interface
    private static native void classInitNative();
    private native void initNative();
    private native void stopNative();
    private native void cleanupNative();
    private native void createBroadcastNative(byte[] metadata, int profile, byte[] broadcastCode);
    private native void updateMetadataNative(int instanceId, byte[] metadata);
    private native void startBroadcastNative(int instanceId);
    private native void stopBroadcastNative(int instanceId);
    private native void pauseBroadcastNative(int instanceId);
    private native void destroyBroadcastNative(int instanceId);
    private native void getBroadcastIdNative(int instanceId);
    private native void getAllBroadcastStatesNative();
}
+217 −0

File changed.

Preview size limit exceeded, changes collapsed.

+97 −0

File changed.

Preview size limit exceeded, changes collapsed.

+244 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading