Loading android/app/jni/com_android_bluetooth_vc.cpp +6 −6 Original line number Diff line number Diff line Loading @@ -66,7 +66,7 @@ class VolumeControlCallbacksImpl : public VolumeControlCallbacks { } void OnVolumeStateChanged(const RawAddress& bd_addr, uint8_t volume, bool mute) override { bool mute, bool isAutonomous) override { LOG(INFO) << __func__; std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); Loading @@ -83,11 +83,11 @@ class VolumeControlCallbacksImpl : public VolumeControlCallbacks { sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVolumeStateChanged, (jint)volume, (jboolean)mute, addr.get()); (jint)volume, (jboolean)mute, addr.get(), (jboolean)isAutonomous); } void OnGroupVolumeStateChanged(int group_id, uint8_t volume, bool mute) override { bool mute, bool isAutonomous) override { LOG(INFO) << __func__; std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); Loading @@ -96,7 +96,7 @@ class VolumeControlCallbacksImpl : public VolumeControlCallbacks { sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGroupVolumeStateChanged, (jint)volume, (jboolean)mute, group_id); (jboolean)mute, group_id, (jboolean)isAutonomous); } }; Loading @@ -107,10 +107,10 @@ static void classInitNative(JNIEnv* env, jclass clazz) { env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V"); method_onVolumeStateChanged = env->GetMethodID(clazz, "onVolumeStateChanged", "(IZ[B)V"); env->GetMethodID(clazz, "onVolumeStateChanged", "(IZ[BZ)V"); method_onGroupVolumeStateChanged = env->GetMethodID(clazz, "onGroupVolumeStateChanged", "(IZI)V"); env->GetMethodID(clazz, "onGroupVolumeStateChanged", "(IZIZ)V"); LOG(INFO) << __func__ << ": succeeds"; } Loading android/app/src/com/android/bluetooth/vc/VolumeControlNativeInterface.java +6 −2 Original line number Diff line number Diff line Loading @@ -154,7 +154,8 @@ public class VolumeControlNativeInterface { sendMessageToService(event); } private void onVolumeStateChanged(int volume, boolean mute, byte[] address) { private void onVolumeStateChanged(int volume, boolean mute, byte[] address, boolean isAutonomous) { VolumeControlStackEvent event = new VolumeControlStackEvent( VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED); Loading @@ -162,6 +163,7 @@ public class VolumeControlNativeInterface { event.valueInt1 = -1; event.valueInt2 = volume; event.valueBool1 = mute; event.valueBool2 = isAutonomous; if (DBG) { Log.d(TAG, "onVolumeStateChanged: " + event); Loading @@ -169,7 +171,8 @@ public class VolumeControlNativeInterface { sendMessageToService(event); } private void onGroupVolumeStateChanged(int volume, boolean mute, int groupId) { private void onGroupVolumeStateChanged(int volume, boolean mute, int groupId, boolean isAutonomous) { VolumeControlStackEvent event = new VolumeControlStackEvent( VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED); Loading @@ -177,6 +180,7 @@ public class VolumeControlNativeInterface { event.valueInt1 = groupId; event.valueInt2 = volume; event.valueBool1 = mute; event.valueBool2 = isAutonomous; if (DBG) { Log.d(TAG, "onGroupVolumeStateChanged: " + event); Loading android/app/src/com/android/bluetooth/vc/VolumeControlService.java +62 −6 Original line number Diff line number Diff line Loading @@ -32,6 +32,8 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioDeviceAttributes; import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.os.HandlerThread; import android.os.ParcelUuid; Loading @@ -56,12 +58,20 @@ public class VolumeControlService extends ProfileService { // Upper limit of all VolumeControl devices: Bonded or Connected private static final int MAX_VC_STATE_MACHINES = 10; private static final int LE_AUDIO_MAX_VOL = 255; private static final int LE_AUDIO_MIN_VOL = 0; private static VolumeControlService sVolumeControlService; private AdapterService mAdapterService; private HandlerThread mStateMachinesThread; private BluetoothDevice mPreviousAudioDevice; private int mMusicMaxVolume = 0; private int mMusicMinVolume = 0; private int mVoiceCallMaxVolume = 0; private int mVoiceCallMinVolume = 0; @VisibleForTesting VolumeControlNativeInterface mVolumeControlNativeInterface; @VisibleForTesting Loading Loading @@ -106,6 +116,11 @@ public class VolumeControlService extends ProfileService { Objects.requireNonNull(mAudioManager, "AudioManager cannot be null when VolumeControlService starts"); mMusicMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); mMusicMinVolume = mAudioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC); mVoiceCallMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL); mVoiceCallMinVolume = mAudioManager.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL); // Start handler thread for state machines mStateMachines.clear(); mStateMachinesThread = new HandlerThread("VolumeControlService.StateMachines"); Loading Loading @@ -422,18 +437,51 @@ public class VolumeControlService extends ProfileService { } void handleVolumeControlChanged(BluetoothDevice device, int groupId, int volume, boolean mute) { /* TODO handle volume change for group in case of unicast le audio * or per device in case of broadcast or simple remote controller. * Note: minimum volume is 0 and maximum 255. */ int volume, boolean mute, boolean isAutonomous) { if (!isAutonomous) { // If the change is triggered by Android device, the stream is already changed. return; } // TODO: Handle the other arguments: device, groupId, mute. int streamType = getBluetoothContextualVolumeStream(); mAudioManager.setStreamVolume(streamType, getDeviceVolume(streamType, volume), AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME); } int getDeviceVolume(int streamType, int bleVolume) { int bleMaxVolume = 255; // min volume is zero int deviceMaxVolume = (streamType == AudioManager.STREAM_VOICE_CALL) ? mVoiceCallMaxVolume : mMusicMaxVolume; int deviceMinVolume = (streamType == AudioManager.STREAM_VOICE_CALL) ? mVoiceCallMinVolume : mMusicMinVolume; // TODO: Investigate what happens in classic BT when BT volume is changed to zero. return (int) Math.floor( (double) bleVolume * (deviceMaxVolume - deviceMinVolume) / bleMaxVolume); } // Copied from AudioService.getBluetoothContextualVolumeStream() and modified it. int getBluetoothContextualVolumeStream() { int mode = mAudioManager.getMode(); switch (mode) { case AudioManager.MODE_IN_COMMUNICATION: case AudioManager.MODE_IN_CALL: return AudioManager.STREAM_VOICE_CALL; case AudioManager.MODE_NORMAL: default: // other conditions will influence the stream type choice, read on... break; } return AudioManager.STREAM_MUSIC; } void messageFromNative(VolumeControlStackEvent stackEvent) { if (stackEvent.type == VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED) { handleVolumeControlChanged(stackEvent.device, stackEvent.valueInt1, stackEvent.valueInt2, stackEvent.valueBool1); stackEvent.valueInt2, stackEvent.valueBool1, stackEvent.valueBool2); return; } Loading Loading @@ -495,6 +543,14 @@ public class VolumeControlService extends ProfileService { sm = VolumeControlStateMachine.make(device, this, mVolumeControlNativeInterface, mStateMachinesThread.getLooper()); mStateMachines.put(device, sm); mAudioManager.setDeviceVolumeBehavior( new AudioDeviceAttributes( AudioDeviceAttributes.ROLE_OUTPUT, // Currently, TYPE_BLUETOOTH_A2DP is the only thing that works. AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, ""), AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE); return sm; } } Loading android/app/src/com/android/bluetooth/vc/VolumeControlStackEvent.java +12 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ public class VolumeControlStackEvent { public int valueInt1; public int valueInt2; public boolean valueBool1; public boolean valueBool2; /* Might need more for other callbacks*/ VolumeControlStackEvent(int type) { Loading @@ -52,6 +53,7 @@ public class VolumeControlStackEvent { result.append(", valueInt1:" + eventTypeValue1ToString(type, valueInt1)); result.append(", valueInt2:" + eventTypeValue2ToString(type, valueInt2)); result.append(", valueBool1:" + eventTypeValueBool1ToString(type, valueBool1)); result.append(", valueBool2:" + eventTypeValueBool2ToString(type, valueBool2)); result.append("}"); return result.toString(); } Loading Loading @@ -124,4 +126,14 @@ public class VolumeControlStackEvent { } return Boolean.toString(value); } private static String eventTypeValueBool2ToString(int type, boolean value) { switch (type) { case EVENT_TYPE_VOLUME_STATE_CHANGED: return "{isAutonomous:" + value + "}"; default: break; } return Boolean.toString(value); } } system/bta/vc/types.h +3 −1 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ struct VolumeOperation { int group_id_; bool started_; bool is_autonomous_; uint8_t opcode_; std::vector<uint8_t> arguments_; Loading @@ -57,11 +58,12 @@ struct VolumeOperation { std::vector<RawAddress> devices_; alarm_t* operation_timeout_; VolumeOperation(int operation_id, int group_id, uint8_t opcode, VolumeOperation(int operation_id, int group_id, bool is_autonomous, uint8_t opcode, std::vector<uint8_t> arguments, std::vector<RawAddress> devices) : operation_id_(operation_id), group_id_(group_id), is_autonomous_(is_autonomous), opcode_(opcode), arguments_(arguments), devices_(devices) { Loading Loading
android/app/jni/com_android_bluetooth_vc.cpp +6 −6 Original line number Diff line number Diff line Loading @@ -66,7 +66,7 @@ class VolumeControlCallbacksImpl : public VolumeControlCallbacks { } void OnVolumeStateChanged(const RawAddress& bd_addr, uint8_t volume, bool mute) override { bool mute, bool isAutonomous) override { LOG(INFO) << __func__; std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); Loading @@ -83,11 +83,11 @@ class VolumeControlCallbacksImpl : public VolumeControlCallbacks { sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVolumeStateChanged, (jint)volume, (jboolean)mute, addr.get()); (jint)volume, (jboolean)mute, addr.get(), (jboolean)isAutonomous); } void OnGroupVolumeStateChanged(int group_id, uint8_t volume, bool mute) override { bool mute, bool isAutonomous) override { LOG(INFO) << __func__; std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); Loading @@ -96,7 +96,7 @@ class VolumeControlCallbacksImpl : public VolumeControlCallbacks { sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGroupVolumeStateChanged, (jint)volume, (jboolean)mute, group_id); (jboolean)mute, group_id, (jboolean)isAutonomous); } }; Loading @@ -107,10 +107,10 @@ static void classInitNative(JNIEnv* env, jclass clazz) { env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V"); method_onVolumeStateChanged = env->GetMethodID(clazz, "onVolumeStateChanged", "(IZ[B)V"); env->GetMethodID(clazz, "onVolumeStateChanged", "(IZ[BZ)V"); method_onGroupVolumeStateChanged = env->GetMethodID(clazz, "onGroupVolumeStateChanged", "(IZI)V"); env->GetMethodID(clazz, "onGroupVolumeStateChanged", "(IZIZ)V"); LOG(INFO) << __func__ << ": succeeds"; } Loading
android/app/src/com/android/bluetooth/vc/VolumeControlNativeInterface.java +6 −2 Original line number Diff line number Diff line Loading @@ -154,7 +154,8 @@ public class VolumeControlNativeInterface { sendMessageToService(event); } private void onVolumeStateChanged(int volume, boolean mute, byte[] address) { private void onVolumeStateChanged(int volume, boolean mute, byte[] address, boolean isAutonomous) { VolumeControlStackEvent event = new VolumeControlStackEvent( VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED); Loading @@ -162,6 +163,7 @@ public class VolumeControlNativeInterface { event.valueInt1 = -1; event.valueInt2 = volume; event.valueBool1 = mute; event.valueBool2 = isAutonomous; if (DBG) { Log.d(TAG, "onVolumeStateChanged: " + event); Loading @@ -169,7 +171,8 @@ public class VolumeControlNativeInterface { sendMessageToService(event); } private void onGroupVolumeStateChanged(int volume, boolean mute, int groupId) { private void onGroupVolumeStateChanged(int volume, boolean mute, int groupId, boolean isAutonomous) { VolumeControlStackEvent event = new VolumeControlStackEvent( VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED); Loading @@ -177,6 +180,7 @@ public class VolumeControlNativeInterface { event.valueInt1 = groupId; event.valueInt2 = volume; event.valueBool1 = mute; event.valueBool2 = isAutonomous; if (DBG) { Log.d(TAG, "onGroupVolumeStateChanged: " + event); Loading
android/app/src/com/android/bluetooth/vc/VolumeControlService.java +62 −6 Original line number Diff line number Diff line Loading @@ -32,6 +32,8 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioDeviceAttributes; import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.os.HandlerThread; import android.os.ParcelUuid; Loading @@ -56,12 +58,20 @@ public class VolumeControlService extends ProfileService { // Upper limit of all VolumeControl devices: Bonded or Connected private static final int MAX_VC_STATE_MACHINES = 10; private static final int LE_AUDIO_MAX_VOL = 255; private static final int LE_AUDIO_MIN_VOL = 0; private static VolumeControlService sVolumeControlService; private AdapterService mAdapterService; private HandlerThread mStateMachinesThread; private BluetoothDevice mPreviousAudioDevice; private int mMusicMaxVolume = 0; private int mMusicMinVolume = 0; private int mVoiceCallMaxVolume = 0; private int mVoiceCallMinVolume = 0; @VisibleForTesting VolumeControlNativeInterface mVolumeControlNativeInterface; @VisibleForTesting Loading Loading @@ -106,6 +116,11 @@ public class VolumeControlService extends ProfileService { Objects.requireNonNull(mAudioManager, "AudioManager cannot be null when VolumeControlService starts"); mMusicMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); mMusicMinVolume = mAudioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC); mVoiceCallMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL); mVoiceCallMinVolume = mAudioManager.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL); // Start handler thread for state machines mStateMachines.clear(); mStateMachinesThread = new HandlerThread("VolumeControlService.StateMachines"); Loading Loading @@ -422,18 +437,51 @@ public class VolumeControlService extends ProfileService { } void handleVolumeControlChanged(BluetoothDevice device, int groupId, int volume, boolean mute) { /* TODO handle volume change for group in case of unicast le audio * or per device in case of broadcast or simple remote controller. * Note: minimum volume is 0 and maximum 255. */ int volume, boolean mute, boolean isAutonomous) { if (!isAutonomous) { // If the change is triggered by Android device, the stream is already changed. return; } // TODO: Handle the other arguments: device, groupId, mute. int streamType = getBluetoothContextualVolumeStream(); mAudioManager.setStreamVolume(streamType, getDeviceVolume(streamType, volume), AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME); } int getDeviceVolume(int streamType, int bleVolume) { int bleMaxVolume = 255; // min volume is zero int deviceMaxVolume = (streamType == AudioManager.STREAM_VOICE_CALL) ? mVoiceCallMaxVolume : mMusicMaxVolume; int deviceMinVolume = (streamType == AudioManager.STREAM_VOICE_CALL) ? mVoiceCallMinVolume : mMusicMinVolume; // TODO: Investigate what happens in classic BT when BT volume is changed to zero. return (int) Math.floor( (double) bleVolume * (deviceMaxVolume - deviceMinVolume) / bleMaxVolume); } // Copied from AudioService.getBluetoothContextualVolumeStream() and modified it. int getBluetoothContextualVolumeStream() { int mode = mAudioManager.getMode(); switch (mode) { case AudioManager.MODE_IN_COMMUNICATION: case AudioManager.MODE_IN_CALL: return AudioManager.STREAM_VOICE_CALL; case AudioManager.MODE_NORMAL: default: // other conditions will influence the stream type choice, read on... break; } return AudioManager.STREAM_MUSIC; } void messageFromNative(VolumeControlStackEvent stackEvent) { if (stackEvent.type == VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED) { handleVolumeControlChanged(stackEvent.device, stackEvent.valueInt1, stackEvent.valueInt2, stackEvent.valueBool1); stackEvent.valueInt2, stackEvent.valueBool1, stackEvent.valueBool2); return; } Loading Loading @@ -495,6 +543,14 @@ public class VolumeControlService extends ProfileService { sm = VolumeControlStateMachine.make(device, this, mVolumeControlNativeInterface, mStateMachinesThread.getLooper()); mStateMachines.put(device, sm); mAudioManager.setDeviceVolumeBehavior( new AudioDeviceAttributes( AudioDeviceAttributes.ROLE_OUTPUT, // Currently, TYPE_BLUETOOTH_A2DP is the only thing that works. AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, ""), AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE); return sm; } } Loading
android/app/src/com/android/bluetooth/vc/VolumeControlStackEvent.java +12 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ public class VolumeControlStackEvent { public int valueInt1; public int valueInt2; public boolean valueBool1; public boolean valueBool2; /* Might need more for other callbacks*/ VolumeControlStackEvent(int type) { Loading @@ -52,6 +53,7 @@ public class VolumeControlStackEvent { result.append(", valueInt1:" + eventTypeValue1ToString(type, valueInt1)); result.append(", valueInt2:" + eventTypeValue2ToString(type, valueInt2)); result.append(", valueBool1:" + eventTypeValueBool1ToString(type, valueBool1)); result.append(", valueBool2:" + eventTypeValueBool2ToString(type, valueBool2)); result.append("}"); return result.toString(); } Loading Loading @@ -124,4 +126,14 @@ public class VolumeControlStackEvent { } return Boolean.toString(value); } private static String eventTypeValueBool2ToString(int type, boolean value) { switch (type) { case EVENT_TYPE_VOLUME_STATE_CHANGED: return "{isAutonomous:" + value + "}"; default: break; } return Boolean.toString(value); } }
system/bta/vc/types.h +3 −1 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ struct VolumeOperation { int group_id_; bool started_; bool is_autonomous_; uint8_t opcode_; std::vector<uint8_t> arguments_; Loading @@ -57,11 +58,12 @@ struct VolumeOperation { std::vector<RawAddress> devices_; alarm_t* operation_timeout_; VolumeOperation(int operation_id, int group_id, uint8_t opcode, VolumeOperation(int operation_id, int group_id, bool is_autonomous, uint8_t opcode, std::vector<uint8_t> arguments, std::vector<RawAddress> devices) : operation_id_(operation_id), group_id_(group_id), is_autonomous_(is_autonomous), opcode_(opcode), arguments_(arguments), devices_(devices) { Loading