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

Commit 7eb3349c authored by Pavlin Radoslavov's avatar Pavlin Radoslavov
Browse files

Update the A2DP Codec Config API

Previously, the JNI upcall would contain only the current codec config.
In the new API, the upcall contains:
 1. The current codec config
 2. The list of codecs containing the local codecs capabilities
 3. The list of codecs containing the selectable codecs capabilities.
    This list is the intersection of the local codecs capabilities
    and the capabilities of the paired device.

Also, refactored the Java internals to accomodate the extra information:
 * Added new class BluetoothCodecStatus that contains the extra info:
   current codec config, local codecs capabilities and selectable
   codecs capabilities
 * Renamed method getCodecConfig() to getCodecStatus() and return the
   corresponding BluetoothCodecStatus object.
 * Updates to class BluetoothCodecConfig:
   new methods isValid(), getCodecName(), and updated toString()
   so it is more user friendly
 * Removed BluetoothCodecConfig.EXTRA_CODEC_CONFIG and
   EXTRA_PREVIOUS_CODEC_CONFIG.
   The former is superseded by BluetoothCodecStatus.EXTRA_CODEC_STATUS;
   the latter is not really used.

Test: A2DP streaming with headsets and switching the codecs
Change-Id: I18536cffc5c73abc31c3d6afa54f9d3c3b1d577e
parent d550d3bc
Loading
Loading
Loading
Loading
+69 −8
Original line number Diff line number Diff line
@@ -30,6 +30,11 @@ static jmethodID method_onConnectionStateChanged;
static jmethodID method_onAudioStateChanged;
static jmethodID method_onCodecConfigChanged;

static struct {
  jclass clazz;
  jmethodID constructor;
} android_bluetooth_BluetoothCodecConfig;

static const btav_source_interface_t* sBluetoothA2dpInterface = NULL;
static jobject mCallbacksObj = NULL;

@@ -73,19 +78,58 @@ static void bta2dp_audio_state_callback(btav_audio_state_t state,

static void bta2dp_audio_config_callback(
    btav_a2dp_codec_config_t codec_config,
    std::vector<btav_a2dp_codec_config_t> codec_capabilities) {
    std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities,
    std::vector<btav_a2dp_codec_config_t> codecs_selectable_capabilities) {
  ALOGI("%s", __func__);
  CallbackEnv sCallbackEnv(__func__);
  if (!sCallbackEnv.valid()) return;

  sCallbackEnv->CallVoidMethod(
      mCallbacksObj, method_onCodecConfigChanged, (jint)codec_config.codec_type,
      (jint)codec_config.codec_priority, (jint)codec_config.sample_rate,
      (jint)codec_config.bits_per_sample, (jint)codec_config.channel_mode,
      (jlong)codec_config.codec_specific_1,
  jobject codecConfigObj = sCallbackEnv->NewObject(
      android_bluetooth_BluetoothCodecConfig.clazz,
      android_bluetooth_BluetoothCodecConfig.constructor,
      (jint)codec_config.codec_type, (jint)codec_config.codec_priority,
      (jint)codec_config.sample_rate, (jint)codec_config.bits_per_sample,
      (jint)codec_config.channel_mode, (jlong)codec_config.codec_specific_1,
      (jlong)codec_config.codec_specific_2,
      (jlong)codec_config.codec_specific_3,
      (jlong)codec_config.codec_specific_4);

  jsize i = 0;
  jobjectArray local_capabilities_array = sCallbackEnv->NewObjectArray(
      (jsize)codecs_local_capabilities.size(),
      android_bluetooth_BluetoothCodecConfig.clazz, NULL);
  for (auto const& cap : codecs_local_capabilities) {
    jobject capObj = sCallbackEnv->NewObject(
        android_bluetooth_BluetoothCodecConfig.clazz,
        android_bluetooth_BluetoothCodecConfig.constructor,
        (jint)cap.codec_type, (jint)cap.codec_priority, (jint)cap.sample_rate,
        (jint)cap.bits_per_sample, (jint)cap.channel_mode,
        (jlong)cap.codec_specific_1, (jlong)cap.codec_specific_2,
        (jlong)cap.codec_specific_3, (jlong)cap.codec_specific_4);
    sCallbackEnv->SetObjectArrayElement(local_capabilities_array, i++, capObj);
    sCallbackEnv->DeleteLocalRef(capObj);
  }

  i = 0;
  jobjectArray selectable_capabilities_array = sCallbackEnv->NewObjectArray(
      (jsize)codecs_selectable_capabilities.size(),
      android_bluetooth_BluetoothCodecConfig.clazz, NULL);
  for (auto const& cap : codecs_selectable_capabilities) {
    jobject capObj = sCallbackEnv->NewObject(
        android_bluetooth_BluetoothCodecConfig.clazz,
        android_bluetooth_BluetoothCodecConfig.constructor,
        (jint)cap.codec_type, (jint)cap.codec_priority, (jint)cap.sample_rate,
        (jint)cap.bits_per_sample, (jint)cap.channel_mode,
        (jlong)cap.codec_specific_1, (jlong)cap.codec_specific_2,
        (jlong)cap.codec_specific_3, (jlong)cap.codec_specific_4);
    sCallbackEnv->SetObjectArrayElement(selectable_capabilities_array, i++,
                                        capObj);
    sCallbackEnv->DeleteLocalRef(capObj);
  }

  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onCodecConfigChanged,
                               codecConfigObj, local_capabilities_array,
                               selectable_capabilities_array);
}

static btav_source_callbacks_t sBluetoothA2dpCallbacks = {
@@ -94,14 +138,21 @@ static btav_source_callbacks_t sBluetoothA2dpCallbacks = {
};

static void classInitNative(JNIEnv* env, jclass clazz) {
  jclass jniBluetoothCodecConfigClass =
      env->FindClass("android/bluetooth/BluetoothCodecConfig");
  android_bluetooth_BluetoothCodecConfig.constructor =
      env->GetMethodID(jniBluetoothCodecConfigClass, "<init>", "(IIIIIJJJJ)V");

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

  method_onAudioStateChanged =
      env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V");

  method_onCodecConfigChanged =
      env->GetMethodID(clazz, "onCodecConfigChanged", "(IIIIIJJJJ)V");
  method_onCodecConfigChanged = env->GetMethodID(
      clazz, "onCodecConfigChanged",
      "(Landroid/bluetooth/BluetoothCodecConfig;[Landroid/bluetooth/"
      "BluetoothCodecConfig;[Landroid/bluetooth/BluetoothCodecConfig;)V");

  ALOGI("%s: succeeds", __func__);
}
@@ -130,6 +181,13 @@ static void initNative(JNIEnv* env, jobject object) {
    return;
  }

  android_bluetooth_BluetoothCodecConfig.clazz = (jclass)env->NewGlobalRef(
      env->FindClass("android/bluetooth/BluetoothCodecConfig"));
  if (android_bluetooth_BluetoothCodecConfig.clazz == nullptr) {
    ALOGE("Failed to allocate Global Ref for BluetoothCodecConfig class");
    return;
  }

  sBluetoothA2dpInterface =
      (btav_source_interface_t*)btInf->get_profile_interface(
          BT_PROFILE_ADVANCED_AUDIO_ID);
@@ -158,6 +216,9 @@ static void cleanupNative(JNIEnv* env, jobject object) {
    sBluetoothA2dpInterface = NULL;
  }

  env->DeleteGlobalRef(android_bluetooth_BluetoothCodecConfig.clazz);
  android_bluetooth_BluetoothCodecConfig.clazz = nullptr;

  if (mCallbacksObj != NULL) {
    env->DeleteGlobalRef(mCallbacksObj);
    mCallbacksObj = NULL;
+6 −5
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.bluetooth.a2dp;

import android.bluetooth.BluetoothCodecConfig;
import android.bluetooth.BluetoothCodecStatus;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
@@ -224,10 +225,10 @@ public class A2dpService extends ProfileService {
        return mStateMachine.isPlaying(device);
    }

    public BluetoothCodecConfig getCodecConfig() {
    public BluetoothCodecStatus getCodecStatus() {
        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
        if (DBG) Log.d(TAG, "getCodecConfig()");
        return mStateMachine.getCodecConfig();
        if (DBG) Log.d(TAG, "getCodecStatus()");
        return mStateMachine.getCodecStatus();
    }

    public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) {
@@ -328,10 +329,10 @@ public class A2dpService extends ProfileService {
            return service.isA2dpPlaying(device);
        }

        public BluetoothCodecConfig getCodecConfig() {
        public BluetoothCodecStatus getCodecStatus() {
            A2dpService service = getService();
            if (service == null) return null;
            return service.getCodecConfig();
            return service.getCodecStatus();
        }

        public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) {
+21 −15
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ package com.android.bluetooth.a2dp;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothCodecConfig;
import android.bluetooth.BluetoothCodecStatus;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
@@ -103,7 +104,7 @@ final class A2dpStateMachine extends StateMachine {
    private BluetoothDevice mIncomingDevice = null;
    private BluetoothDevice mPlayingA2dpDevice = null;

    private BluetoothCodecConfig mCodecConfig = null;
    private BluetoothCodecStatus mCodecStatus = null;

    static {
        classInitNative();
@@ -654,30 +655,35 @@ final class A2dpStateMachine extends StateMachine {
        return false;
    }

    BluetoothCodecConfig getCodecConfig() {
    BluetoothCodecStatus getCodecStatus() {
        synchronized (this) {
            return mCodecConfig;
            return mCodecStatus;
        }
    }

    private void onCodecConfigChanged(int codecType, int codecPriority, int sampleRate,
            int bitsPerSample, int channelMode, long codecSpecific1, long codecSpecific2,
            long codecSpecific3, long codecSpecific4) {
        BluetoothCodecConfig prevCodecConfig;
        BluetoothCodecConfig newCodecConfig = new BluetoothCodecConfig(codecType, codecPriority,
                sampleRate, bitsPerSample, channelMode, codecSpecific1, codecSpecific2,
                codecSpecific3, codecSpecific4);
    private void onCodecConfigChanged(BluetoothCodecConfig newCodecConfig,
            BluetoothCodecConfig[] codecsLocalCapabilities,
            BluetoothCodecConfig[] codecsSelectableCapabilities) {
        BluetoothCodecConfig prevCodecConfig = null;
        synchronized (this) {
            prevCodecConfig = mCodecConfig;
            mCodecConfig = newCodecConfig;
            if (mCodecStatus != null) {
                prevCodecConfig = mCodecStatus.getCodecConfig();
            }
            mCodecStatus = new BluetoothCodecStatus(
                    newCodecConfig, codecsLocalCapabilities, codecsSelectableCapabilities);
        }

        Intent intent = new Intent(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED);
        intent.putExtra(BluetoothCodecConfig.EXTRA_CODEC_CONFIG, newCodecConfig);
        intent.putExtra(BluetoothCodecConfig.EXTRA_PREVIOUS_CODEC_CONFIG, prevCodecConfig);
        intent.putExtra(BluetoothCodecStatus.EXTRA_CODEC_STATUS, mCodecStatus);
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);

        log("A2DP Codec Config: " + prevCodecConfig + "->" + newCodecConfig);
        for (BluetoothCodecConfig codecConfig : codecsLocalCapabilities) {
            log("A2DP Codec Local Capability: " + codecConfig);
        }
        for (BluetoothCodecConfig codecConfig : codecsSelectableCapabilities) {
            log("A2DP Codec Selectable Capability: " + codecConfig);
        }

        // Inform the Audio Service about the codec configuration change,
        // so the Audio Service can reset accordingly the audio feeding