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

Commit a0810686 authored by Pavlin Radoslavov's avatar Pavlin Radoslavov
Browse files

Add a mechanism for configuring the A2DP Source codecs

* Extended the Bluetooth A2DP AIDL interface to get/set the current
  codec configuration

* If the current codec config is changed such that the audio feeding
  parameters have changed, then inform the Audio Manager so it can
  reset accordingly the audio feeding parameters in the Audio HAL to
  the Bluetooth stack.

Also, fix the BT_BOND_STATE_* values (unused) in the
AbstractionLayer.java class so they match the corresponding
BT_BOND_STATE_* values in
hardware/libhardware/include/hardware/bluetooth.h

Test: A2DP streaming to headsets, TestPlans/71390
Bug: 30958229
Change-Id: Ic3e35154d9dbc32b66522322cd5d2a96bc5a977c
parent d17ac215
Loading
Loading
Loading
Loading
+57 −5
Original line number Original line Diff line number Diff line
@@ -28,8 +28,9 @@
namespace android {
namespace android {
static jmethodID method_onConnectionStateChanged;
static jmethodID method_onConnectionStateChanged;
static jmethodID method_onAudioStateChanged;
static jmethodID method_onAudioStateChanged;
static jmethodID method_onCodecConfigChanged;


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


static void bta2dp_connection_state_callback(btav_connection_state_t state,
static void bta2dp_connection_state_callback(btav_connection_state_t state,
@@ -70,9 +71,26 @@ static void bta2dp_audio_state_callback(btav_audio_state_t state,
  sCallbackEnv->DeleteLocalRef(addr);
  sCallbackEnv->DeleteLocalRef(addr);
}
}


static btav_callbacks_t sBluetoothA2dpCallbacks = {
static void bta2dp_audio_config_callback(
    btav_a2dp_codec_config_t codec_config,
    std::vector<btav_a2dp_codec_config_t> codec_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,
      (jlong)codec_config.codec_specific_2,
      (jlong)codec_config.codec_specific_3,
      (jlong)codec_config.codec_specific_4);
}

static btav_source_callbacks_t sBluetoothA2dpCallbacks = {
    sizeof(sBluetoothA2dpCallbacks), bta2dp_connection_state_callback,
    sizeof(sBluetoothA2dpCallbacks), bta2dp_connection_state_callback,
    bta2dp_audio_state_callback, NULL, /* audio_config_cb */
    bta2dp_audio_state_callback, bta2dp_audio_config_callback,
};
};


static void classInitNative(JNIEnv* env, jclass clazz) {
static void classInitNative(JNIEnv* env, jclass clazz) {
@@ -82,6 +100,9 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
  method_onAudioStateChanged =
  method_onAudioStateChanged =
      env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V");
      env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V");


  method_onCodecConfigChanged =
      env->GetMethodID(clazz, "onCodecConfigChanged", "(IIIIIJJJJ)V");

  ALOGI("%s: succeeds", __func__);
  ALOGI("%s: succeeds", __func__);
}
}


@@ -109,7 +130,8 @@ static void initNative(JNIEnv* env, jobject object) {
    return;
    return;
  }
  }


  sBluetoothA2dpInterface = (btav_interface_t*)btInf->get_profile_interface(
  sBluetoothA2dpInterface =
      (btav_source_interface_t*)btInf->get_profile_interface(
          BT_PROFILE_ADVANCED_AUDIO_ID);
          BT_PROFILE_ADVANCED_AUDIO_ID);
  if (sBluetoothA2dpInterface == NULL) {
  if (sBluetoothA2dpInterface == NULL) {
    ALOGE("Failed to get Bluetooth A2DP Interface");
    ALOGE("Failed to get Bluetooth A2DP Interface");
@@ -179,12 +201,42 @@ static jboolean disconnectA2dpNative(JNIEnv* env, jobject object,
  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
}


static jboolean setCodecConfigPreferenceNative(
    JNIEnv* env, jobject object, jint codecType, jint codecPriority,
    jint sampleRate, jint bitsPerSample, jint channelMode, jlong codecSpecific1,
    jlong codecSpecific2, jlong codecSpecific3, jlong codecSpecific4) {
  if (!sBluetoothA2dpInterface) return JNI_FALSE;

  btav_a2dp_codec_config_t codec_config = {
      .codec_type = static_cast<btav_a2dp_codec_index_t>(codecType),
      .codec_priority = static_cast<btav_a2dp_codec_priority_t>(codecPriority),
      .sample_rate = static_cast<btav_a2dp_codec_sample_rate_t>(sampleRate),
      .bits_per_sample =
          static_cast<btav_a2dp_codec_bits_per_sample_t>(bitsPerSample),
      .channel_mode = static_cast<btav_a2dp_codec_channel_mode_t>(channelMode),
      .codec_specific_1 = codecSpecific1,
      .codec_specific_2 = codecSpecific2,
      .codec_specific_3 = codecSpecific3,
      .codec_specific_4 = codecSpecific4};

  std::vector<btav_a2dp_codec_config_t> codec_preferences;
  codec_preferences.push_back(codec_config);

  bt_status_t status = sBluetoothA2dpInterface->config_codec(codec_preferences);
  if (status != BT_STATUS_SUCCESS) {
    ALOGE("Failed codec configuration, status: %d", status);
  }
  return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}

static JNINativeMethod sMethods[] = {
static JNINativeMethod sMethods[] = {
    {"classInitNative", "()V", (void*)classInitNative},
    {"classInitNative", "()V", (void*)classInitNative},
    {"initNative", "()V", (void*)initNative},
    {"initNative", "()V", (void*)initNative},
    {"cleanupNative", "()V", (void*)cleanupNative},
    {"cleanupNative", "()V", (void*)cleanupNative},
    {"connectA2dpNative", "([B)Z", (void*)connectA2dpNative},
    {"connectA2dpNative", "([B)Z", (void*)connectA2dpNative},
    {"disconnectA2dpNative", "([B)Z", (void*)disconnectA2dpNative},
    {"disconnectA2dpNative", "([B)Z", (void*)disconnectA2dpNative},
    {"setCodecConfigPreferenceNative", "(IIIIIJJJJ)Z",
     (void*)setCodecConfigPreferenceNative},
};
};


int register_com_android_bluetooth_a2dp(JNIEnv* env) {
int register_com_android_bluetooth_a2dp(JNIEnv* env) {
+5 −4
Original line number Original line Diff line number Diff line
@@ -30,7 +30,7 @@ static jmethodID method_onConnectionStateChanged;
static jmethodID method_onAudioStateChanged;
static jmethodID method_onAudioStateChanged;
static jmethodID method_onAudioConfigChanged;
static jmethodID method_onAudioConfigChanged;


static const btav_interface_t* sBluetoothA2dpInterface = NULL;
static const btav_sink_interface_t* sBluetoothA2dpInterface = NULL;
static jobject mCallbacksObj = NULL;
static jobject mCallbacksObj = NULL;


static void bta2dp_connection_state_callback(btav_connection_state_t state,
static void bta2dp_connection_state_callback(btav_connection_state_t state,
@@ -91,7 +91,7 @@ static void bta2dp_audio_config_callback(bt_bdaddr_t* bd_addr,
  sCallbackEnv->DeleteLocalRef(addr);
  sCallbackEnv->DeleteLocalRef(addr);
}
}


static btav_callbacks_t sBluetoothA2dpCallbacks = {
static btav_sink_callbacks_t sBluetoothA2dpCallbacks = {
    sizeof(sBluetoothA2dpCallbacks), bta2dp_connection_state_callback,
    sizeof(sBluetoothA2dpCallbacks), bta2dp_connection_state_callback,
    bta2dp_audio_state_callback, bta2dp_audio_config_callback,
    bta2dp_audio_state_callback, bta2dp_audio_config_callback,
};
};
@@ -128,7 +128,8 @@ static void initNative(JNIEnv* env, jobject object) {
    mCallbacksObj = NULL;
    mCallbacksObj = NULL;
  }
  }


  sBluetoothA2dpInterface = (btav_interface_t*)btInf->get_profile_interface(
  sBluetoothA2dpInterface =
      (btav_sink_interface_t*)btInf->get_profile_interface(
          BT_PROFILE_ADVANCED_AUDIO_SINK_ID);
          BT_PROFILE_ADVANCED_AUDIO_SINK_ID);
  if (sBluetoothA2dpInterface == NULL) {
  if (sBluetoothA2dpInterface == NULL) {
    ALOGE("Failed to get Bluetooth A2DP Sink Interface");
    ALOGE("Failed to get Bluetooth A2DP Sink Interface");
+26 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package com.android.bluetooth.a2dp;
package com.android.bluetooth.a2dp;


import android.bluetooth.BluetoothCodecConfig;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.BluetoothUuid;
@@ -28,6 +29,7 @@ import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.Utils;
import com.android.bluetooth.Utils;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.List;
import java.util.List;
import java.util.Objects;


/**
/**
 * Provides Bluetooth A2DP profile, as a service in the Bluetooth application.
 * Provides Bluetooth A2DP profile, as a service in the Bluetooth application.
@@ -222,6 +224,18 @@ public class A2dpService extends ProfileService {
        return mStateMachine.isPlaying(device);
        return mStateMachine.isPlaying(device);
    }
    }


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

    public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) {
        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
        if (DBG) Log.d(TAG, "setCodecConfigPreference(): " + Objects.toString(codecConfig));
        mStateMachine.setCodecConfigPreference(codecConfig);
    }

    //Binder object: Must be static class or memory leak may occur 
    //Binder object: Must be static class or memory leak may occur 
    private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub 
    private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub 
        implements IProfileServiceBinder {
        implements IProfileServiceBinder {
@@ -313,6 +327,18 @@ public class A2dpService extends ProfileService {
            if (service == null) return false;
            if (service == null) return false;
            return service.isA2dpPlaying(device);
            return service.isA2dpPlaying(device);
        }
        }

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

        public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) {
            A2dpService service = getService();
            if (service == null) return;
            service.setCodecConfigPreference(codecConfig);
        }
    };
    };


    @Override
    @Override
+52 −2
Original line number Original line Diff line number Diff line
@@ -30,6 +30,7 @@ package com.android.bluetooth.a2dp;


import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothCodecConfig;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.BluetoothUuid;
@@ -102,6 +103,7 @@ final class A2dpStateMachine extends StateMachine {
    private BluetoothDevice mIncomingDevice = null;
    private BluetoothDevice mIncomingDevice = null;
    private BluetoothDevice mPlayingA2dpDevice = null;
    private BluetoothDevice mPlayingA2dpDevice = null;


    private BluetoothCodecConfig mCodecConfig = null;


    static {
    static {
        classInitNative();
        classInitNative();
@@ -664,6 +666,51 @@ final class A2dpStateMachine extends StateMachine {
        return false;
        return false;
    }
    }


    BluetoothCodecConfig getCodecConfig() {
        synchronized (this) {
            return mCodecConfig;
        }
    }

    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);
        synchronized (this) {
            prevCodecConfig = mCodecConfig;
            mCodecConfig = newCodecConfig;
        }

        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.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);

        log("A2DP Codec Config : " + prevCodecConfig + "->" + newCodecConfig);

        // Inform the Audio Service about the codec configuration change,
        // so the Audio Service can reset accordingly the audio feeding
        // parameters in the Audio HAL to the Bluetooth stack.
        if (!newCodecConfig.sameAudioFeedingParameters(prevCodecConfig) && (mCurrentDevice != null)
                && (getCurrentState() == mConnected)) {
            // Add the device only if it is currently connected
            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mCurrentDevice);
            mAudioManager.handleBluetoothA2dpDeviceConfigChange(mCurrentDevice);
        }
        mContext.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM);
    }

    void setCodecConfigPreference(BluetoothCodecConfig codecConfig) {
        setCodecConfigPreferenceNative(codecConfig.getCodecType(), codecConfig.getCodecPriority(),
                codecConfig.getSampleRate(), codecConfig.getBitsPerSample(),
                codecConfig.getChannelMode(), codecConfig.getCodecSpecific1(),
                codecConfig.getCodecSpecific2(), codecConfig.getCodecSpecific3(),
                codecConfig.getCodecSpecific4());
    }

    boolean okToConnect(BluetoothDevice device) {
    boolean okToConnect(BluetoothDevice device) {
        AdapterService adapterService = AdapterService.getAdapterService();
        AdapterService adapterService = AdapterService.getAdapterService();
        int priority = mService.getPriority(device);
        int priority = mService.getPriority(device);
@@ -818,4 +865,7 @@ final class A2dpStateMachine extends StateMachine {
    private native void cleanupNative();
    private native void cleanupNative();
    private native boolean connectA2dpNative(byte[] address);
    private native boolean connectA2dpNative(byte[] address);
    private native boolean disconnectA2dpNative(byte[] address);
    private native boolean disconnectA2dpNative(byte[] address);
    private native boolean setCodecConfigPreferenceNative(int codecType, int codecPriority,
            int sampleRate, int bitsPerSample, int channelMode, long codecSpecific1,
            long codecSpecific2, long codecSpecific3, long codecSpecific4);
}
}
+2 −1
Original line number Original line Diff line number Diff line
@@ -53,7 +53,8 @@ final public class AbstractionLayer {
    static final int BT_DEVICE_TYPE_DUAL = 0x03;
    static final int BT_DEVICE_TYPE_DUAL = 0x03;


    static final int BT_BOND_STATE_NONE = 0x00;
    static final int BT_BOND_STATE_NONE = 0x00;
    static final int BT_BOND_STATE_BONDED = 0x01;
    static final int BT_BOND_STATE_BONDING = 0x01;
    static final int BT_BOND_STATE_BONDED = 0x02;


    static final int BT_SSP_VARIANT_PASSKEY_CONFIRMATION = 0x00;
    static final int BT_SSP_VARIANT_PASSKEY_CONFIRMATION = 0x00;
    static final int BT_SSP_VARIANT_PASSKEY_ENTRY = 0x01;
    static final int BT_SSP_VARIANT_PASSKEY_ENTRY = 0x01;