audio: Add IBluetoothLe
Similar to IBluetooth and IBluetoothA2dp interfaces,
IBluetoothLe controls the LE profile. This interface
replaces the following string parameter:
"LeAudioSuspended"
Bug: 270731693
Test: atest VtsHalAudioCoreTargetTest
Change-Id: I9df28053cd270fc2d446ba9a8ada4dd6ab761c7b
diff --git a/audio/aidl/Android.bp b/audio/aidl/Android.bp
index bb48ecd..515787e 100644
--- a/audio/aidl/Android.bp
+++ b/audio/aidl/Android.bp
@@ -114,6 +114,7 @@
"android/hardware/audio/core/AudioRoute.aidl",
"android/hardware/audio/core/IBluetooth.aidl",
"android/hardware/audio/core/IBluetoothA2dp.aidl",
+ "android/hardware/audio/core/IBluetoothLe.aidl",
"android/hardware/audio/core/IConfig.aidl",
"android/hardware/audio/core/IModule.aidl",
"android/hardware/audio/core/IStreamCallback.aidl",
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IBluetoothLe.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IBluetoothLe.aidl
new file mode 100644
index 0000000..f29e1fd
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IBluetoothLe.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.core;
+@VintfStability
+interface IBluetoothLe {
+ boolean isEnabled();
+ void setEnabled(boolean enabled);
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl
index f18d6a5..e14e9c0 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl
@@ -38,6 +38,7 @@
@nullable android.hardware.audio.core.ITelephony getTelephony();
@nullable android.hardware.audio.core.IBluetooth getBluetooth();
@nullable android.hardware.audio.core.IBluetoothA2dp getBluetoothA2dp();
+ @nullable android.hardware.audio.core.IBluetoothLe getBluetoothLe();
android.media.audio.common.AudioPort connectExternalDevice(in android.media.audio.common.AudioPort templateIdAndAdditionalData);
void disconnectExternalDevice(int portId);
android.hardware.audio.core.AudioPatch[] getAudioPatches();
diff --git a/audio/aidl/android/hardware/audio/core/IBluetooth.aidl b/audio/aidl/android/hardware/audio/core/IBluetooth.aidl
index 21ac8e2..7fa2e24 100644
--- a/audio/aidl/android/hardware/audio/core/IBluetooth.aidl
+++ b/audio/aidl/android/hardware/audio/core/IBluetooth.aidl
@@ -25,6 +25,9 @@
* and the SCO Link. This interface is optional to implement and provide by the
* vendor. It needs to be provided only if the device actually supports BT SCO
* or HFP.
+ *
+ * Each of IBluetooth* interfaces is independent of each other. The HAL module
+ * can provide any combination of them.
*/
@VintfStability
interface IBluetooth {
diff --git a/audio/aidl/android/hardware/audio/core/IBluetoothA2dp.aidl b/audio/aidl/android/hardware/audio/core/IBluetoothA2dp.aidl
index dc4b8d0..c4dd738 100644
--- a/audio/aidl/android/hardware/audio/core/IBluetoothA2dp.aidl
+++ b/audio/aidl/android/hardware/audio/core/IBluetoothA2dp.aidl
@@ -24,8 +24,8 @@
* the vendor. It needs to be provided only if the device actually supports BT
* A2DP.
*
- * This interface is separate from IBluetooth interface which manages SCO & HFP.
- * The HAL module can handle both SCO and A2DP profiles or only one of them.
+ * Each of IBluetooth* interfaces is independent of each other. The HAL module
+ * can provide any combination of them.
*/
@VintfStability
interface IBluetoothA2dp {
diff --git a/audio/aidl/android/hardware/audio/core/IBluetoothLe.aidl b/audio/aidl/android/hardware/audio/core/IBluetoothLe.aidl
new file mode 100644
index 0000000..272f862
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/IBluetoothLe.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * 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.
+ */
+
+package android.hardware.audio.core;
+
+/**
+ * An instance of IBluetoothLe manages settings for the LE (Low Energy)
+ * profiles. This interface is optional to implement by the vendor. It needs to
+ * be provided only if the device actually supports BT LE.
+ *
+ * Each of IBluetooth* interfaces is independent of each other. The HAL module
+ * can provide any combination of them.
+ */
+@VintfStability
+interface IBluetoothLe {
+ /**
+ * Whether BT LE is enabled.
+ *
+ * Returns the current state of LE support. The client might need to
+ * disable (suspend) LE when another profile (for example, SCO) is
+ * activated.
+ *
+ * @return Whether BT LE is enabled.
+ */
+ boolean isEnabled();
+
+ /**
+ * Enable or disable LE.
+ *
+ * Sets the current state of LE support. The client might need to
+ * disable (suspend) LE when another Bluetooth profile (for example, SCO) is
+ * activated.
+ *
+ * @param enabled Whether BT LE must be enabled or suspended.
+ * @throws EX_ILLEGAL_STATE If there was an error performing the operation.
+ */
+ void setEnabled(boolean enabled);
+}
diff --git a/audio/aidl/android/hardware/audio/core/IModule.aidl b/audio/aidl/android/hardware/audio/core/IModule.aidl
index 080d826..7830501 100644
--- a/audio/aidl/android/hardware/audio/core/IModule.aidl
+++ b/audio/aidl/android/hardware/audio/core/IModule.aidl
@@ -22,6 +22,7 @@
import android.hardware.audio.core.AudioRoute;
import android.hardware.audio.core.IBluetooth;
import android.hardware.audio.core.IBluetoothA2dp;
+import android.hardware.audio.core.IBluetoothLe;
import android.hardware.audio.core.IStreamCallback;
import android.hardware.audio.core.IStreamIn;
import android.hardware.audio.core.IStreamOut;
@@ -118,6 +119,20 @@
@nullable IBluetoothA2dp getBluetoothA2dp();
/**
+ * Retrieve the interface to control Bluetooth LE.
+ *
+ * If the HAL module supports LE Profile functionality for Bluetooth, it
+ * must return an instance of the IBluetoothLe interface. The same
+ * instance must be returned during the lifetime of the HAL module. If the
+ * HAL module does not support BT LE, a null must be returned, without
+ * throwing any errors.
+ *
+ * @return An instance of the IBluetoothLe interface implementation.
+ * @throws EX_ILLEGAL_STATE If there was an error creating an instance.
+ */
+ @nullable IBluetoothLe getBluetoothLe();
+
+ /**
* Set a device port of an external device into connected state.
*
* This method is used to inform the HAL module that an external device has
@@ -842,7 +857,7 @@
AudioMMapPolicyInfo[] getMmapPolicyInfos(AudioMMapPolicyType mmapPolicyType);
/**
- * Indicates if this module supports variable latency control for instance
+ * Indicates if this module supports variable latency control, for instance,
* over Bluetooth A2DP or LE Audio links.
*
* If supported, all instances of IStreamOut interface returned by this module must
diff --git a/audio/aidl/default/Bluetooth.cpp b/audio/aidl/default/Bluetooth.cpp
index bd9a864..8115e7b 100644
--- a/audio/aidl/default/Bluetooth.cpp
+++ b/audio/aidl/default/Bluetooth.cpp
@@ -105,4 +105,16 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus BluetoothLe::isEnabled(bool* _aidl_return) {
+ *_aidl_return = mEnabled;
+ LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus BluetoothLe::setEnabled(bool in_enabled) {
+ mEnabled = in_enabled;
+ LOG(DEBUG) << __func__ << ": " << mEnabled;
+ return ndk::ScopedAStatus::ok();
+}
+
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index c4f3844..c95c199 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -396,6 +396,15 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus Module::getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) {
+ if (!mBluetoothLe) {
+ mBluetoothLe = ndk::SharedRefBase::make<BluetoothLe>();
+ }
+ *_aidl_return = mBluetoothLe.getPtr();
+ LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get();
+ return ndk::ScopedAStatus::ok();
+}
+
ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdAndAdditionalData,
AudioPort* _aidl_return) {
const int32_t templateId = in_templateIdAndAdditionalData.id;
diff --git a/audio/aidl/default/include/core-impl/Bluetooth.h b/audio/aidl/default/include/core-impl/Bluetooth.h
index e2f48ba..1cd0355 100644
--- a/audio/aidl/default/include/core-impl/Bluetooth.h
+++ b/audio/aidl/default/include/core-impl/Bluetooth.h
@@ -18,6 +18,7 @@
#include <aidl/android/hardware/audio/core/BnBluetooth.h>
#include <aidl/android/hardware/audio/core/BnBluetoothA2dp.h>
+#include <aidl/android/hardware/audio/core/BnBluetoothLe.h>
namespace aidl::android::hardware::audio::core {
@@ -48,4 +49,15 @@
bool mEnabled = false;
};
+class BluetoothLe : public BnBluetoothLe {
+ public:
+ BluetoothLe() = default;
+
+ private:
+ ndk::ScopedAStatus isEnabled(bool* _aidl_return) override;
+ ndk::ScopedAStatus setEnabled(bool in_enabled) override;
+
+ bool mEnabled = false;
+};
+
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index 0544646..2cbda7d 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -72,6 +72,7 @@
ndk::ScopedAStatus getTelephony(std::shared_ptr<ITelephony>* _aidl_return) override;
ndk::ScopedAStatus getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) override;
ndk::ScopedAStatus getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) override;
+ ndk::ScopedAStatus getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) override;
ndk::ScopedAStatus connectExternalDevice(
const ::aidl::android::media::audio::common::AudioPort& in_templateIdAndAdditionalData,
::aidl::android::media::audio::common::AudioPort* _aidl_return) override;
@@ -175,6 +176,7 @@
ChildInterface<ITelephony> mTelephony;
ChildInterface<IBluetooth> mBluetooth;
ChildInterface<IBluetoothA2dp> mBluetoothA2dp;
+ ChildInterface<IBluetoothLe> mBluetoothLe;
// ids of ports created at runtime via 'connectExternalDevice'.
std::set<int32_t> mConnectedDevicePorts;
Streams mStreams;
diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
index f06c0f1..650a543 100644
--- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
@@ -62,6 +62,7 @@
using aidl::android::hardware::audio::core::AudioRoute;
using aidl::android::hardware::audio::core::IBluetooth;
using aidl::android::hardware::audio::core::IBluetoothA2dp;
+using aidl::android::hardware::audio::core::IBluetoothLe;
using aidl::android::hardware::audio::core::IModule;
using aidl::android::hardware::audio::core::IStreamCommon;
using aidl::android::hardware::audio::core::IStreamIn;
@@ -2111,6 +2112,42 @@
}
}
+class AudioCoreBluetoothLe : public AudioCoreModuleBase,
+ public testing::TestWithParam<std::string> {
+ public:
+ void SetUp() override {
+ ASSERT_NO_FATAL_FAILURE(SetUpImpl(GetParam()));
+ ASSERT_IS_OK(module->getBluetoothLe(&bluetooth));
+ }
+
+ void TearDown() override { ASSERT_NO_FATAL_FAILURE(TearDownImpl()); }
+
+ std::shared_ptr<IBluetoothLe> bluetooth;
+};
+
+TEST_P(AudioCoreBluetoothLe, SameInstance) {
+ if (bluetooth == nullptr) {
+ GTEST_SKIP() << "BluetoothLe is not supported";
+ }
+ std::shared_ptr<IBluetoothLe> bluetooth2;
+ EXPECT_IS_OK(module->getBluetoothLe(&bluetooth2));
+ ASSERT_NE(nullptr, bluetooth2.get());
+ EXPECT_EQ(bluetooth->asBinder(), bluetooth2->asBinder())
+ << "getBluetoothLe must return the same interface instance across invocations";
+}
+
+TEST_P(AudioCoreBluetoothLe, Enabled) {
+ if (bluetooth == nullptr) {
+ GTEST_SKIP() << "BluetoothLe is not supported";
+ }
+ // Since enabling LE may require having an actual device connection,
+ // limit testing to setting back the current value.
+ bool enabled;
+ ASSERT_IS_OK(bluetooth->isEnabled(&enabled));
+ EXPECT_IS_OK(bluetooth->setEnabled(enabled))
+ << "setEnabled without actual state change must not fail";
+}
+
class AudioCoreTelephony : public AudioCoreModuleBase, public testing::TestWithParam<std::string> {
public:
void SetUp() override {
@@ -3543,6 +3580,10 @@
testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
android::PrintInstanceNameToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioCoreBluetoothA2dp);
+INSTANTIATE_TEST_SUITE_P(AudioCoreBluetoothLeTest, AudioCoreBluetoothLe,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
+ android::PrintInstanceNameToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioCoreBluetoothLe);
INSTANTIATE_TEST_SUITE_P(AudioCoreTelephonyTest, AudioCoreTelephony,
testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
android::PrintInstanceNameToString);