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

Commit d7262a1f authored by Łukasz Rymanowski's avatar Łukasz Rymanowski Committed by Automerger Merge Worker
Browse files

leaudio: Add callbacks with codec informations am: 6277b612

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

Change-Id: Iae3b72d3935dae5c6deb3cc7bdc71e2ccafabecd
parents f8509770 6277b612
Loading
Loading
Loading
Loading
+86 −0
Original line number Diff line number Diff line
@@ -46,6 +46,8 @@ static jmethodID method_onGroupStatus;
static jmethodID method_onGroupNodeStatus;
static jmethodID method_onAudioConf;
static jmethodID method_onSinkAudioLocationAvailable;
static jmethodID method_onAudioLocalCodecCapabilities;
static jmethodID method_onAudioGroupCodecConf;

static struct {
  jclass clazz;
@@ -59,6 +61,32 @@ static std::shared_timed_mutex interface_mutex;
static jobject mCallbacksObj = nullptr;
static std::shared_timed_mutex callbacks_mutex;

jobject prepareCodecConfigObj(JNIEnv* env,
                              btle_audio_codec_config_t codecConfig) {
  jobject codecConfigObj =
      env->NewObject(android_bluetooth_BluetoothLeAudioCodecConfig.clazz,
                     android_bluetooth_BluetoothLeAudioCodecConfig.constructor,
                     (jint)codecConfig.codec_type, 0, 0, 0, 0, 0, 0, 0, 0);
  return codecConfigObj;
}

jobjectArray prepareArrayOfCodecConfigs(
    JNIEnv* env, std::vector<btle_audio_codec_config_t> codecConfigs) {
  jsize i = 0;
  jobjectArray CodecConfigArray = env->NewObjectArray(
      (jsize)codecConfigs.size(),
      android_bluetooth_BluetoothLeAudioCodecConfig.clazz, nullptr);

  for (auto const& cap : codecConfigs) {
    jobject Obj = prepareCodecConfigObj(env, cap);

    env->SetObjectArrayElement(CodecConfigArray, i++, Obj);
    env->DeleteLocalRef(Obj);
  }

  return CodecConfigArray;
}

class LeAudioClientCallbacksImpl : public LeAudioClientCallbacks {
 public:
  ~LeAudioClientCallbacksImpl() = default;
@@ -152,6 +180,54 @@ class LeAudioClientCallbacksImpl : public LeAudioClientCallbacks {
                                 method_onSinkAudioLocationAvailable,
                                 addr.get(), (jint)sink_audio_location);
  }

  void OnAudioLocalCodecCapabilities(
      std::vector<btle_audio_codec_config_t> local_input_capa_codec_conf,
      std::vector<btle_audio_codec_config_t> local_output_capa_codec_conf)
      override {
    LOG(INFO) << __func__;

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

    jobject localInputCapCodecConfigArray = prepareArrayOfCodecConfigs(
        sCallbackEnv.get(), local_input_capa_codec_conf);

    jobject localOutputCapCodecConfigArray = prepareArrayOfCodecConfigs(
        sCallbackEnv.get(), local_output_capa_codec_conf);

    sCallbackEnv->CallVoidMethod(
        mCallbacksObj, method_onAudioLocalCodecCapabilities,
        localInputCapCodecConfigArray, localOutputCapCodecConfigArray);
  }

  void OnAudioGroupCodecConf(
      int group_id, btle_audio_codec_config_t input_codec_conf,
      btle_audio_codec_config_t output_codec_conf,
      std::vector<btle_audio_codec_config_t> input_selectable_codec_conf,
      std::vector<btle_audio_codec_config_t> output_selectable_codec_conf)
      override {
    LOG(INFO) << __func__;

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

    jobject inputCodecConfigObj =
        prepareCodecConfigObj(sCallbackEnv.get(), input_codec_conf);
    jobject outputCodecConfigObj =
        prepareCodecConfigObj(sCallbackEnv.get(), input_codec_conf);
    jobject inputSelectableCodecConfigArray = prepareArrayOfCodecConfigs(
        sCallbackEnv.get(), input_selectable_codec_conf);
    jobject outputSelectableCodecConfigArray = prepareArrayOfCodecConfigs(
        sCallbackEnv.get(), output_selectable_codec_conf);

    sCallbackEnv->CallVoidMethod(
        mCallbacksObj, method_onAudioGroupCodecConf, (jint)group_id,
        inputCodecConfigObj, outputCodecConfigObj,
        inputSelectableCodecConfigArray, outputSelectableCodecConfigArray);
  }
};

static LeAudioClientCallbacksImpl sLeAudioClientCallbacks;
@@ -172,6 +248,16 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
      env->GetMethodID(clazz, "onSinkAudioLocationAvailable", "([BI)V");
  method_onConnectionStateChanged =
      env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
  method_onAudioLocalCodecCapabilities =
      env->GetMethodID(clazz, "onAudioLocalCodecCapabilities",
                       "([Landroid/bluetooth/BluetoothLeAudioCodecConfig;"
                       "[Landroid/bluetooth/BluetoothLeAudioCodecConfig;)V");
  method_onAudioGroupCodecConf =
      env->GetMethodID(clazz, "onAudioGroupCodecConf",
                       "(ILandroid/bluetooth/BluetoothLeAudioCodecConfig;"
                       "Landroid/bluetooth/BluetoothLeAudioCodecConfig;"
                       "[Landroid/bluetooth/BluetoothLeAudioCodecConfig;"
                       "[Landroid/bluetooth/BluetoothLeAudioCodecConfig;)V");
}

std::vector<btle_audio_codec_config_t> prepareCodecPreferences(
+38 −0
Original line number Diff line number Diff line
@@ -30,6 +30,8 @@ import android.util.Log;
import com.android.bluetooth.Utils;
import com.android.internal.annotations.GuardedBy;

import java.util.Arrays;

/**
 * LeAudio Native Interface to/from JNI.
 */
@@ -153,6 +155,42 @@ public class LeAudioNativeInterface {
        sendMessageToService(event);
    }

    private void onAudioLocalCodecCapabilities(
                            BluetoothLeAudioCodecConfig[] localInputCodecCapabilities,
                            BluetoothLeAudioCodecConfig[] localOutputCodecCapabilities) {
        LeAudioStackEvent event =
                new LeAudioStackEvent(
                        LeAudioStackEvent.EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED);

        event.valueCodecList1 = Arrays.asList(localInputCodecCapabilities);
        event.valueCodecList2 = Arrays.asList(localOutputCodecCapabilities);

        if (DBG) {
            Log.d(TAG, "onAudioLocalCodecCapabilities: " + event);
        }
        sendMessageToService(event);
    }

    private void onAudioGroupCodecConf(int groupId, BluetoothLeAudioCodecConfig inputCodecConfig,
                            BluetoothLeAudioCodecConfig outputCodecConfig,
                            BluetoothLeAudioCodecConfig [] inputSelectableCodecConfig,
                            BluetoothLeAudioCodecConfig [] outputSelectableCodecConfig) {
        LeAudioStackEvent event =
                new LeAudioStackEvent(
                        LeAudioStackEvent.EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED);

        event.valueInt1 = groupId;
        event.valueCodec1 = inputCodecConfig;
        event.valueCodec2 = outputCodecConfig;
        event.valueCodecList1 = Arrays.asList(inputSelectableCodecConfig);
        event.valueCodecList2 = Arrays.asList(outputSelectableCodecConfig);

        if (DBG) {
            Log.d(TAG, "onAudioGroupCodecConf: " + event);
        }
        sendMessageToService(event);
    }

    /**
     * Initializes the native interface.
     *
+34 −0
Original line number Diff line number Diff line
@@ -141,6 +141,9 @@ public class LeAudioService extends ProfileService {
        public BluetoothLeAudioCodecStatus mCodecStatus;
    }

    List<BluetoothLeAudioCodecConfig> mInputLocalCodecCapabilities = new ArrayList<>();
    List<BluetoothLeAudioCodecConfig> mOutputLocalCodecCapabilities = new ArrayList<>();

    private final Map<Integer, LeAudioGroupDescriptor> mGroupDescriptors = new LinkedHashMap<>();
    private final Map<BluetoothDevice, LeAudioStateMachine> mStateMachines = new LinkedHashMap<>();

@@ -1029,6 +1032,37 @@ public class LeAudioService extends ProfileService {
                default:
                    break;
            }
        } else if (stackEvent.type
                        == LeAudioStackEvent.EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED) {
            mInputLocalCodecCapabilities = stackEvent.valueCodecList1;
            mOutputLocalCodecCapabilities = stackEvent.valueCodecList2;
        } else if (stackEvent.type
                        == LeAudioStackEvent.EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED) {
            int groupId = stackEvent.valueInt1;
            LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(groupId);
            if (descriptor == null) {
                Log.e(TAG, " Group not found " + groupId);
                return;
            }

            BluetoothLeAudioCodecStatus status =
                    new BluetoothLeAudioCodecStatus(stackEvent.valueCodec1,
                            stackEvent.valueCodec2, mInputLocalCodecCapabilities,
                            mOutputLocalCodecCapabilities,
                            stackEvent.valueCodecList1,
                            stackEvent.valueCodecList2);

            if (DBG) {
                if (descriptor.mCodecStatus != null) {
                    Log.d(TAG, " Replacing codec status for group: " + groupId);
                } else {
                    Log.d(TAG, " New codec status for group: " + groupId);
                }
            }

            descriptor.mCodecStatus = status;
            notifyUnicastCodecConfigChanged(groupId, status);

        } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED) {
            int direction = stackEvent.valueInt1;
            int group_id = stackEvent.valueInt2;
+65 −1
Original line number Diff line number Diff line
@@ -18,7 +18,9 @@
package com.android.bluetooth.le_audio;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeAudioCodecConfig;

import java.util.List;
/**
 * Stack event sent via a callback from JNI to Java, or generated
 * internally by the LeAudio State Machine.
@@ -31,8 +33,10 @@ public class LeAudioStackEvent {
    public static final int EVENT_TYPE_GROUP_NODE_STATUS_CHANGED = 3;
    public static final int EVENT_TYPE_AUDIO_CONF_CHANGED = 4;
    public static final int EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE = 5;
    public static final int EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED = 6;
    public static final int EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED = 7;
        // -------- DO NOT PUT ANY NEW UNICAST EVENTS BELOW THIS LINE-------------
    public static final int EVENT_TYPE_UNICAST_MAX = 6;
    public static final int EVENT_TYPE_UNICAST_MAX = 8;

    // Broadcast related events
    public static final int EVENT_TYPE_BROADCAST_CREATED = EVENT_TYPE_UNICAST_MAX + 1;
@@ -70,6 +74,10 @@ public class LeAudioStackEvent {
    public int valueInt5 = 0;
    public boolean valueBool1 = false;
    public byte[] valueByte1;
    public BluetoothLeAudioCodecConfig valueCodec1;
    public BluetoothLeAudioCodecConfig valueCodec2;
    public List<BluetoothLeAudioCodecConfig> valueCodecList1;
    public List<BluetoothLeAudioCodecConfig> valueCodecList2;

    LeAudioStackEvent(int type) {
        this.type = type;
@@ -87,6 +95,12 @@ public class LeAudioStackEvent {
        result.append(", value4:" + eventTypeValue4ToString(type, valueInt4));
        result.append(", value5:" + eventTypeValue5ToString(type, valueInt5));
        result.append(", valueBool1:" + eventTypeValueBool1ToString(type, valueBool1));
        result.append(", valueCodec1:" + eventTypeValueCodec1ToString(type, valueCodec1));
        result.append(", valueCodec2:" + eventTypeValueCodec2ToString(type, valueCodec2));
        result.append(", valueCodecList1:"
                + eventTypeValueCodecList1ToString(type, valueCodecList1));
        result.append(", valueCodecList2:"
                + eventTypeValueCodecList2ToString(type, valueCodecList2));
        result.append(", " + eventTypeValueByte1ToString(type, valueByte1));
        result.append("}");
        return result.toString();
@@ -114,6 +128,10 @@ public class LeAudioStackEvent {
                return "EVENT_TYPE_BROADCAST_STATE";
            case EVENT_TYPE_BROADCAST_ID:
                return "EVENT_TYPE_BROADCAST_ID";
            case EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED:
                return "EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED";
            case EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED:
                return "EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED";
            default:
                return "EVENT_TYPE_UNKNOWN:" + type;
        }
@@ -136,6 +154,8 @@ public class LeAudioStackEvent {
                }
            case EVENT_TYPE_GROUP_NODE_STATUS_CHANGED:
                // same as EVENT_TYPE_GROUP_STATUS_CHANGED
            case EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED:
                // same as EVENT_TYPE_GROUP_STATUS_CHANGED
            case EVENT_TYPE_GROUP_STATUS_CHANGED:
                return "{group_id:" + Integer.toString(value) + "}";
            case EVENT_TYPE_AUDIO_CONF_CHANGED:
@@ -243,6 +263,50 @@ public class LeAudioStackEvent {
        }
    }

    private static String eventTypeValueCodec1ToString(int type,
                                    BluetoothLeAudioCodecConfig value) {
        switch (type) {
            case EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED:
                return "{input codec = " + value + "}";
            default:
                return "<unused>";
        }
    }

    private static String eventTypeValueCodec2ToString(int type,
                                    BluetoothLeAudioCodecConfig value) {
        switch (type) {
            case EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED:
                return "{output codec = " + value + "}";
            default:
                return "<unused>";
        }
    }

    private static String eventTypeValueCodecList1ToString(int type,
                                    List<BluetoothLeAudioCodecConfig> value) {
        switch (type) {
            case EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED:
                return "{input local capa codec = " + value + "}";
            case EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED:
                return "{input selectable codec = " + value + "}";
            default:
                return "<unused>";
        }
    }

    private static String eventTypeValueCodecList2ToString(int type,
                                    List<BluetoothLeAudioCodecConfig> value) {
        switch (type) {
            case EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED:
                return "{output local capa codec = " + value + "}";
            case EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED:
                return "{output selectable codec = " + value + "}";
            default:
                return "<unused>";
        }
    }

    private static String broadcastStateToString(int state) {
        switch (state) {
            case BROADCAST_STATE_STOPPED:
+115 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import static org.mockito.Mockito.eq;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothLeAudioCodecConfig;
import android.bluetooth.BluetoothLeAudioCodecStatus;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
@@ -67,6 +68,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -93,6 +95,8 @@ public class LeAudioServiceTest {
    private LinkedBlockingQueue<Intent> mGroupIntentQueue = new LinkedBlockingQueue<>();
    private int testGroupId = 1;
    private boolean onGroupStatusCallbackCalled = false;
    private boolean onGroupCodecConfChangedCallbackCalled = false;
    private BluetoothLeAudioCodecStatus testCodecStatus = null;

    private BroadcastReceiver mLeAudioIntentReceiver;

@@ -103,6 +107,45 @@ public class LeAudioServiceTest {

    @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();

    private static final BluetoothLeAudioCodecConfig LC3_16KHZ_CONFIG =
            new BluetoothLeAudioCodecConfig.Builder()
                .setCodecType(BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3)
                .setSampleRate(BluetoothLeAudioCodecConfig.SAMPLE_RATE_16000)
                .build();
    private static final BluetoothLeAudioCodecConfig LC3_48KHZ_CONFIG =
            new BluetoothLeAudioCodecConfig.Builder()
                .setCodecType(BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3)
                .setSampleRate(BluetoothLeAudioCodecConfig.SAMPLE_RATE_48000)
                .build();

    private static final BluetoothLeAudioCodecConfig LC3_48KHZ_16KHZ_CONFIG =
             new BluetoothLeAudioCodecConfig.Builder()
               .setCodecType(BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3)
               .setSampleRate(BluetoothLeAudioCodecConfig.SAMPLE_RATE_48000
                                | BluetoothLeAudioCodecConfig.SAMPLE_RATE_16000)
               .build();

    private static final List<BluetoothLeAudioCodecConfig> INPUT_CAPABILITIES_CONFIG =
            new ArrayList() {{
                    add(LC3_48KHZ_16KHZ_CONFIG);
            }};

    private static final List<BluetoothLeAudioCodecConfig> OUTPUT_CAPABILITIES_CONFIG =
            new ArrayList() {{
                    add(LC3_48KHZ_16KHZ_CONFIG);
            }};


    private static final List<BluetoothLeAudioCodecConfig> INPUT_SELECTABLE_CONFIG =
            new ArrayList() {{
                    add(LC3_16KHZ_CONFIG);
             }};

    private static final List<BluetoothLeAudioCodecConfig> OUTPUT_SELECTABLE_CONFIG =
            new ArrayList() {{
                    add(LC3_48KHZ_16KHZ_CONFIG);
            }};

    @Before
    public void setUp() throws Exception {
        mTargetContext = InstrumentationRegistry.getTargetContext();
@@ -1101,4 +1144,76 @@ public class LeAudioServiceTest {
        sendEventAndVerifyIntentForGroupStatusChanged(testGroupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE);
    }

    private void injectLocalCodecConfigCapaChanged(List<BluetoothLeAudioCodecConfig> inputCodecCapa,
                                                 List<BluetoothLeAudioCodecConfig> outputCodecCapa) {
        int eventType = LeAudioStackEvent.EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED;

        // Add device to group
        LeAudioStackEvent localCodecCapaEvent = new LeAudioStackEvent(eventType);
        localCodecCapaEvent.valueCodecList1 = inputCodecCapa;
        localCodecCapaEvent.valueCodecList2 =  outputCodecCapa;
        mService.messageFromNative(localCodecCapaEvent);
    }

    private void injectGroupCodecConfigChanged(int groupId, BluetoothLeAudioCodecConfig inputCodecConfig,
                                BluetoothLeAudioCodecConfig outputCodecConfig,
                                List<BluetoothLeAudioCodecConfig> inputSelectableCodecConfig,
                                List<BluetoothLeAudioCodecConfig> outputSelectableCodecConfig) {
        int eventType = LeAudioStackEvent.EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED;

        // Add device to group
        LeAudioStackEvent groupCodecConfigChangedEvent = new LeAudioStackEvent(eventType);
        groupCodecConfigChangedEvent.valueInt1 = groupId;
        groupCodecConfigChangedEvent.valueCodec1 = inputCodecConfig;
        groupCodecConfigChangedEvent.valueCodec2 = outputCodecConfig;
        groupCodecConfigChangedEvent.valueCodecList1 = inputSelectableCodecConfig;
        groupCodecConfigChangedEvent.valueCodecList2 =  outputSelectableCodecConfig;
        mService.messageFromNative(groupCodecConfigChangedEvent);
    }

    /**
     * Test native interface group status message handling
     */
    @Test
    public void testMessageFromNativeGroupCodecConfigChanged() {
        onGroupCodecConfChangedCallbackCalled = false;

        injectLocalCodecConfigCapaChanged(INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG);

        doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
        connectTestDevice(mSingleDevice, testGroupId);

        testCodecStatus = new BluetoothLeAudioCodecStatus(LC3_16KHZ_CONFIG,
                                LC3_48KHZ_CONFIG, INPUT_CAPABILITIES_CONFIG,
                                OUTPUT_CAPABILITIES_CONFIG, INPUT_SELECTABLE_CONFIG,
                                OUTPUT_SELECTABLE_CONFIG);

        IBluetoothLeAudioCallback leAudioCallbacks =
        new IBluetoothLeAudioCallback.Stub() {
            @Override
            public void onCodecConfigChanged(int gid, BluetoothLeAudioCodecStatus status) {
                onGroupCodecConfChangedCallbackCalled = true;
                assertThat(status.equals(testCodecStatus)).isTrue();
            }
            @Override
            public void onGroupStatusChanged(int gid, int gStatus) {}
            @Override
            public void onGroupNodeAdded(BluetoothDevice device, int gid) {}
            @Override
            public void onGroupNodeRemoved(BluetoothDevice device, int gid) {}
        };

        mService.mLeAudioCallbacks.register(leAudioCallbacks);

        injectGroupCodecConfigChanged(testGroupId, LC3_16KHZ_CONFIG, LC3_48KHZ_CONFIG,
                                        INPUT_SELECTABLE_CONFIG,
                                        OUTPUT_SELECTABLE_CONFIG);


        TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper());
        assertThat(onGroupCodecConfChangedCallbackCalled).isTrue();

        onGroupCodecConfChangedCallbackCalled = false;
        mService.mLeAudioCallbacks.unregister(leAudioCallbacks);
    }
}
Loading