Merge "cameraservice_test: Create host-side build."
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.cpp b/media/libaaudio/src/client/AAudioFlowGraph.cpp
index 5b46ae0..d0c3238 100644
--- a/media/libaaudio/src/client/AAudioFlowGraph.cpp
+++ b/media/libaaudio/src/client/AAudioFlowGraph.cpp
@@ -42,14 +42,15 @@
audio_format_t sinkFormat,
int32_t sinkChannelCount,
bool useMonoBlend,
- float audioBalance) {
+ float audioBalance,
+ bool isExclusive) {
FlowGraphPortFloatOutput *lastOutput = nullptr;
// TODO change back to ALOGD
ALOGI("%s() source format = 0x%08x, channels = %d, sink format = 0x%08x, channels = %d, "
- "useMonoBlend = %d, audioBalance = %f",
+ "useMonoBlend = %d, audioBalance = %f, isExclusive %d",
__func__, sourceFormat, sourceChannelCount, sinkFormat, sinkChannelCount,
- useMonoBlend, audioBalance);
+ useMonoBlend, audioBalance, isExclusive);
switch (sourceFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
@@ -94,22 +95,25 @@
return AAUDIO_ERROR_UNIMPLEMENTED;
}
- // Apply volume ramps to set the left/right audio balance and target volumes.
- // The signals will be decoupled, volume ramps will be applied, before the signals are
- // combined again.
- mMultiToManyConverter = std::make_unique<MultiToManyConverter>(sinkChannelCount);
- mManyToMultiConverter = std::make_unique<ManyToMultiConverter>(sinkChannelCount);
- lastOutput->connect(&mMultiToManyConverter->input);
- for (int i = 0; i < sinkChannelCount; i++) {
- mVolumeRamps.emplace_back(std::make_unique<RampLinear>(1));
- mPanningVolumes.emplace_back(1.0f);
- lastOutput = mMultiToManyConverter->outputs[i].get();
- lastOutput->connect(&(mVolumeRamps[i].get()->input));
- lastOutput = &(mVolumeRamps[i].get()->output);
- lastOutput->connect(mManyToMultiConverter->inputs[i].get());
+ // Apply volume ramps for only exclusive streams.
+ if (isExclusive) {
+ // Apply volume ramps to set the left/right audio balance and target volumes.
+ // The signals will be decoupled, volume ramps will be applied, before the signals are
+ // combined again.
+ mMultiToManyConverter = std::make_unique<MultiToManyConverter>(sinkChannelCount);
+ mManyToMultiConverter = std::make_unique<ManyToMultiConverter>(sinkChannelCount);
+ lastOutput->connect(&mMultiToManyConverter->input);
+ for (int i = 0; i < sinkChannelCount; i++) {
+ mVolumeRamps.emplace_back(std::make_unique<RampLinear>(1));
+ mPanningVolumes.emplace_back(1.0f);
+ lastOutput = mMultiToManyConverter->outputs[i].get();
+ lastOutput->connect(&(mVolumeRamps[i].get()->input));
+ lastOutput = &(mVolumeRamps[i].get()->output);
+ lastOutput->connect(mManyToMultiConverter->inputs[i].get());
+ }
+ lastOutput = &mManyToMultiConverter->output;
+ setAudioBalance(audioBalance);
}
- lastOutput = &mManyToMultiConverter->output;
- setAudioBalance(audioBalance);
switch (sinkFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.h b/media/libaaudio/src/client/AAudioFlowGraph.h
index 2056b70..00b6575 100644
--- a/media/libaaudio/src/client/AAudioFlowGraph.h
+++ b/media/libaaudio/src/client/AAudioFlowGraph.h
@@ -43,6 +43,7 @@
* @param useMonoBlend
* @param audioBalance
* @param channelMask
+ * @param isExclusive
* @return
*/
aaudio_result_t configure(audio_format_t sourceFormat,
@@ -50,7 +51,8 @@
audio_format_t sinkFormat,
int32_t sinkChannelCount,
bool useMonoBlend,
- float audioBalance);
+ float audioBalance,
+ bool isExclusive);
void process(const void *source, void *destination, int32_t numFrames);
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 8292573..450d390 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -54,7 +54,8 @@
getDeviceFormat(),
getDeviceChannelCount(),
getRequireMonoBlend(),
- getAudioBalance());
+ getAudioBalance(),
+ (getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE));
if (result != AAUDIO_OK) {
safeReleaseClose();
diff --git a/media/libaudioclient/AidlConversion.cpp b/media/libaudioclient/AidlConversion.cpp
index f81aa87..323e002 100644
--- a/media/libaudioclient/AidlConversion.cpp
+++ b/media/libaudioclient/AidlConversion.cpp
@@ -3314,4 +3314,53 @@
return trackSecondaryOutputInfo;
}
+ConversionResult<audio_direct_mode_t>
+aidl2legacy_AudioDirectMode_audio_direct_mode_t(media::AudioDirectMode aidl) {
+ switch (aidl) {
+ case media::AudioDirectMode::NONE:
+ return AUDIO_DIRECT_NOT_SUPPORTED;
+ case media::AudioDirectMode::OFFLOAD:
+ return AUDIO_DIRECT_OFFLOAD_SUPPORTED;
+ case media::AudioDirectMode::OFFLOAD_GAPLESS:
+ return AUDIO_DIRECT_OFFLOAD_GAPLESS_SUPPORTED;
+ case media::AudioDirectMode::BITSTREAM:
+ return AUDIO_DIRECT_BITSTREAM_SUPPORTED;
+ }
+ return unexpected(BAD_VALUE);
+}
+ConversionResult<media::AudioDirectMode>
+legacy2aidl_audio_direct_mode_t_AudioDirectMode(audio_direct_mode_t legacy) {
+ switch (legacy) {
+ case AUDIO_DIRECT_NOT_SUPPORTED:
+ return media::AudioDirectMode::NONE;
+ case AUDIO_DIRECT_OFFLOAD_SUPPORTED:
+ return media::AudioDirectMode::OFFLOAD;
+ case AUDIO_DIRECT_OFFLOAD_GAPLESS_SUPPORTED:
+ return media::AudioDirectMode::OFFLOAD_GAPLESS;
+ case AUDIO_DIRECT_BITSTREAM_SUPPORTED:
+ return media::AudioDirectMode::BITSTREAM;
+ }
+ return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_direct_mode_t> aidl2legacy_int32_t_audio_direct_mode_t_mask(int32_t aidl) {
+ using LegacyMask = std::underlying_type_t<audio_direct_mode_t>;
+
+ LegacyMask converted = VALUE_OR_RETURN(
+ (convertBitmask<LegacyMask, int32_t, audio_direct_mode_t, media::AudioDirectMode>(
+ aidl, aidl2legacy_AudioDirectMode_audio_direct_mode_t,
+ indexToEnum_index<media::AudioDirectMode>,
+ enumToMask_bitmask<LegacyMask, audio_direct_mode_t>)));
+ return static_cast<audio_direct_mode_t>(converted);
+}
+ConversionResult<int32_t> legacy2aidl_audio_direct_mode_t_int32_t_mask(audio_direct_mode_t legacy) {
+ using LegacyMask = std::underlying_type_t<audio_direct_mode_t>;
+
+ LegacyMask legacyMask = static_cast<LegacyMask>(legacy);
+ return convertBitmask<int32_t, LegacyMask, media::AudioDirectMode, audio_direct_mode_t>(
+ legacyMask, legacy2aidl_audio_direct_mode_t_AudioDirectMode,
+ indexToEnum_bitmask<audio_direct_mode_t>,
+ enumToMask_index<int32_t, media::AudioDirectMode>);
+}
+
} // namespace android
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index ab75c97..9733cb3 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -314,6 +314,7 @@
srcs: [
"aidl/android/media/AudioAttributesInternal.aidl",
"aidl/android/media/AudioClient.aidl",
+ "aidl/android/media/AudioDirectMode.aidl",
"aidl/android/media/AudioDualMonoMode.aidl",
"aidl/android/media/AudioFlag.aidl",
"aidl/android/media/AudioGainSys.aidl",
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 07ef246..b3c82787 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -2289,6 +2289,31 @@
return OK;
}
+status_t AudioSystem::getDirectPlaybackSupport(const audio_attributes_t *attr,
+ const audio_config_t *config,
+ audio_direct_mode_t* directMode) {
+ if (attr == nullptr || config == nullptr || directMode == nullptr) {
+ return BAD_VALUE;
+ }
+
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) {
+ return PERMISSION_DENIED;
+ }
+
+ media::AudioAttributesInternal attrAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_attributes_t_AudioAttributesInternal(*attr));
+ AudioConfig configAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_config_t_AudioConfig(*config, false /*isInput*/));
+
+ media::AudioDirectMode retAidl;
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ aps->getDirectPlaybackSupport(attrAidl, configAidl, &retAidl)));
+ *directMode = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_direct_mode_t_mask(
+ static_cast<int32_t>(retAidl)));
+ return NO_ERROR;
+}
+
class CaptureStateListenerImpl : public media::BnCaptureStateListener,
public IBinder::DeathRecipient {
diff --git a/media/libaudioclient/AudioTrackShared.cpp b/media/libaudioclient/AudioTrackShared.cpp
index da27dc8..e3b79b2 100644
--- a/media/libaudioclient/AudioTrackShared.cpp
+++ b/media/libaudioclient/AudioTrackShared.cpp
@@ -490,6 +490,8 @@
status_t AudioTrackClientProxy::waitStreamEndDone(const struct timespec *requested)
{
struct timespec total; // total elapsed time spent waiting
+ struct timespec before;
+ bool beforeIsValid = false;
total.tv_sec = 0;
total.tv_nsec = 0;
audio_track_cblk_t* cblk = mCblk;
@@ -570,17 +572,38 @@
}
int32_t old = android_atomic_and(~CBLK_FUTEX_WAKE, &cblk->mFutex);
if (!(old & CBLK_FUTEX_WAKE)) {
+ if (!beforeIsValid) {
+ clock_gettime(CLOCK_MONOTONIC, &before);
+ beforeIsValid = true;
+ }
errno = 0;
(void) syscall(__NR_futex, &cblk->mFutex,
mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old & ~CBLK_FUTEX_WAKE, ts);
- switch (errno) {
+ status_t error = errno; // clock_gettime can affect errno
+ {
+ struct timespec after;
+ clock_gettime(CLOCK_MONOTONIC, &after);
+ total.tv_sec += after.tv_sec - before.tv_sec;
+ // Use auto instead of long to avoid the google-runtime-int warning.
+ auto deltaNs = after.tv_nsec - before.tv_nsec;
+ if (deltaNs < 0) {
+ deltaNs += 1000000000;
+ total.tv_sec--;
+ }
+ if ((total.tv_nsec += deltaNs) >= 1000000000) {
+ total.tv_nsec -= 1000000000;
+ total.tv_sec++;
+ }
+ before = after;
+ }
+ switch (error) {
case 0: // normal wakeup by server, or by binderDied()
case EWOULDBLOCK: // benign race condition with server
case EINTR: // wait was interrupted by signal or other spurious wakeup
case ETIMEDOUT: // time-out expired
break;
default:
- status = errno;
+ status = error;
ALOGE("%s unexpected error %s", __func__, strerror(status));
goto end;
}
diff --git a/media/libaudioclient/aidl/android/media/AudioDirectMode.aidl b/media/libaudioclient/aidl/android/media/AudioDirectMode.aidl
new file mode 100644
index 0000000..0da4721
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioDirectMode.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 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.media;
+
+@Backing(type="int")
+enum AudioDirectMode {
+ NONE = 0,
+ OFFLOAD = 1,
+ OFFLOAD_GAPLESS = 2,
+ BITSTREAM = 4,
+}
diff --git a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
index 8e9ff86..7895ae3 100644
--- a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
@@ -20,6 +20,7 @@
import android.media.AudioAttributesEx;
import android.media.AudioAttributesInternal;
+import android.media.AudioDirectMode;
import android.media.AudioMix;
import android.media.AudioOffloadMode;
import android.media.AudioPatch;
@@ -376,4 +377,10 @@
boolean canBeSpatialized(in @nullable AudioAttributesInternal attr,
in @nullable AudioConfig config,
in AudioDevice[] devices);
+
+ /**
+ * Query how the direct playback is currently supported on the device.
+ */
+ AudioDirectMode getDirectPlaybackSupport(in AudioAttributesInternal attr,
+ in AudioConfig config);
}
diff --git a/media/libaudioclient/include/media/AidlConversion.h b/media/libaudioclient/include/media/AidlConversion.h
index a6c93cf..e769303 100644
--- a/media/libaudioclient/include/media/AidlConversion.h
+++ b/media/libaudioclient/include/media/AidlConversion.h
@@ -23,6 +23,7 @@
#include <android/media/AudioAttributesInternal.h>
#include <android/media/AudioClient.h>
+#include <android/media/AudioDirectMode.h>
#include <android/media/AudioDualMonoMode.h>
#include <android/media/AudioFlag.h>
#include <android/media/AudioIoConfigEvent.h>
@@ -445,5 +446,13 @@
legacy2aidl_TrackSecondaryOutputInfoPair_TrackSecondaryOutputInfo(
const TrackSecondaryOutputInfoPair& legacy);
+ConversionResult<audio_direct_mode_t>
+aidl2legacy_AudioDirectMode_audio_direct_mode_t(media::AudioDirectMode aidl);
+ConversionResult<media::AudioDirectMode>
+legacy2aidl_audio_direct_mode_t_AudioDirectMode(audio_direct_mode_t legacy);
+
+ConversionResult<audio_direct_mode_t> aidl2legacy_int32_t_audio_direct_mode_t_mask(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_audio_direct_mode_t_int32_t_mask(audio_direct_mode_t legacy);
+
} // namespace android
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 0e9d48c..11eb070 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -536,6 +536,19 @@
const AudioDeviceTypeAddrVector &devices,
bool *canBeSpatialized);
+ /**
+ * Query how the direct playback is currently supported on the device.
+ * @param attr audio attributes describing the playback use case
+ * @param config audio configuration for the playback
+ * @param directMode out: a set of flags describing how the direct playback is currently
+ * supported on the device
+ * @return NO_ERROR in case of success, DEAD_OBJECT, NO_INIT, BAD_VALUE, PERMISSION_DENIED
+ * in case of error.
+ */
+ static status_t getDirectPlaybackSupport(const audio_attributes_t *attr,
+ const audio_config_t *config,
+ audio_direct_mode_t *directMode);
+
// A listener for capture state changes.
class CaptureStateListener : public virtual RefBase {
diff --git a/media/libaudiofoundation/include/media/AudioContainers.h b/media/libaudiofoundation/include/media/AudioContainers.h
index d352a96..a9c7824 100644
--- a/media/libaudiofoundation/include/media/AudioContainers.h
+++ b/media/libaudiofoundation/include/media/AudioContainers.h
@@ -111,26 +111,6 @@
return types;
}
-// FIXME: This is temporary helper function. Remove this when getting rid of all
-// bit mask usages of audio device types.
-static inline DeviceTypeSet deviceTypesFromBitMask(audio_devices_t types) {
- DeviceTypeSet deviceTypes;
- if ((types & AUDIO_DEVICE_BIT_IN) == 0) {
- for (auto deviceType : AUDIO_DEVICE_OUT_ALL_ARRAY) {
- if ((types & deviceType) == deviceType) {
- deviceTypes.insert(deviceType);
- }
- }
- } else {
- for (auto deviceType : AUDIO_DEVICE_IN_ALL_ARRAY) {
- if ((types & deviceType) == deviceType) {
- deviceTypes.insert(deviceType);
- }
- }
- }
- return deviceTypes;
-}
-
std::string deviceTypesToString(const DeviceTypeSet& deviceTypes);
bool deviceTypesToString(const DeviceTypeSet& deviceTypes, std::string &str);
diff --git a/media/libheadtracking/Android.bp b/media/libheadtracking/Android.bp
index 63b769e..b0563e2 100644
--- a/media/libheadtracking/Android.bp
+++ b/media/libheadtracking/Android.bp
@@ -18,6 +18,7 @@
"PoseRateLimiter.cpp",
"QuaternionUtil.cpp",
"ScreenHeadFusion.cpp",
+ "StillnessDetector.cpp",
"Twist.cpp",
],
export_include_dirs: [
@@ -70,6 +71,7 @@
"PoseRateLimiter-test.cpp",
"QuaternionUtil-test.cpp",
"ScreenHeadFusion-test.cpp",
+ "StillnessDetector-test.cpp",
"Twist-test.cpp",
],
shared_libs: [
diff --git a/media/libheadtracking/HeadTrackingProcessor.cpp b/media/libheadtracking/HeadTrackingProcessor.cpp
index 47f7cf0..257ee42 100644
--- a/media/libheadtracking/HeadTrackingProcessor.cpp
+++ b/media/libheadtracking/HeadTrackingProcessor.cpp
@@ -20,6 +20,7 @@
#include "PoseDriftCompensator.h"
#include "QuaternionUtil.h"
#include "ScreenHeadFusion.h"
+#include "StillnessDetector.h"
namespace android {
namespace media {
@@ -40,6 +41,16 @@
.translationalDriftTimeConstant = options.translationalDriftTimeConstant,
.rotationalDriftTimeConstant = options.rotationalDriftTimeConstant,
}),
+ mHeadStillnessDetector(StillnessDetector::Options{
+ .windowDuration = options.autoRecenterWindowDuration,
+ .translationalThreshold = options.autoRecenterTranslationalThreshold,
+ .rotationalThreshold = options.autoRecenterRotationalThreshold,
+ }),
+ mScreenStillnessDetector(StillnessDetector::Options{
+ .windowDuration = options.screenStillnessWindowDuration,
+ .translationalThreshold = options.screenStillnessTranslationalThreshold,
+ .rotationalThreshold = options.screenStillnessRotationalThreshold,
+ }),
mModeSelector(ModeSelector::Options{.freshnessTimeout = options.freshnessTimeout},
initialMode),
mRateLimiter(PoseRateLimiter::Options{
@@ -77,18 +88,35 @@
}
void calculate(int64_t timestamp) override {
- if (mWorldToHeadTimestamp.has_value()) {
- const Pose3f worldToHead = mHeadPoseDriftCompensator.getOutput();
- mScreenHeadFusion.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead);
- mModeSelector.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead);
- }
-
+ // Handle the screen first, since it might trigger a recentering of the head.
if (mWorldToScreenTimestamp.has_value()) {
const Pose3f worldToLogicalScreen = mScreenPoseDriftCompensator.getOutput();
+ mScreenStillnessDetector.setInput(mWorldToScreenTimestamp.value(),
+ worldToLogicalScreen);
+ bool screenStable = mScreenStillnessDetector.calculate(timestamp);
+ mModeSelector.setScreenStable(mWorldToScreenTimestamp.value(), screenStable);
+ // Whenever the screen is unstable, recenter the head pose.
+ if (!screenStable) {
+ recenter(true, false);
+ }
mScreenHeadFusion.setWorldToScreenPose(mWorldToScreenTimestamp.value(),
worldToLogicalScreen);
}
+ // Handle head.
+ if (mWorldToHeadTimestamp.has_value()) {
+ Pose3f worldToHead = mHeadPoseDriftCompensator.getOutput();
+ mHeadStillnessDetector.setInput(mWorldToHeadTimestamp.value(), worldToHead);
+ // Auto-recenter.
+ if (mHeadStillnessDetector.calculate(timestamp)) {
+ recenter(true, false);
+ worldToHead = mHeadPoseDriftCompensator.getOutput();
+ }
+
+ mScreenHeadFusion.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead);
+ mModeSelector.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead);
+ }
+
auto maybeScreenToHead = mScreenHeadFusion.calculate();
if (maybeScreenToHead.has_value()) {
mModeSelector.setScreenToHeadPose(maybeScreenToHead->timestamp,
@@ -114,9 +142,11 @@
void recenter(bool recenterHead, bool recenterScreen) override {
if (recenterHead) {
mHeadPoseDriftCompensator.recenter();
+ mHeadStillnessDetector.reset();
}
if (recenterScreen) {
mScreenPoseDriftCompensator.recenter();
+ mScreenStillnessDetector.reset();
}
// If a sensor being recentered is included in the current mode, apply rate limiting to
@@ -140,6 +170,8 @@
Pose3f mHeadToStagePose;
PoseDriftCompensator mHeadPoseDriftCompensator;
PoseDriftCompensator mScreenPoseDriftCompensator;
+ StillnessDetector mHeadStillnessDetector;
+ StillnessDetector mScreenStillnessDetector;
ScreenHeadFusion mScreenHeadFusion;
ModeSelector mModeSelector;
PoseRateLimiter mRateLimiter;
diff --git a/media/libheadtracking/ModeSelector-test.cpp b/media/libheadtracking/ModeSelector-test.cpp
index 6247d84..a136e6b 100644
--- a/media/libheadtracking/ModeSelector-test.cpp
+++ b/media/libheadtracking/ModeSelector-test.cpp
@@ -44,6 +44,7 @@
ModeSelector selector(options, HeadTrackingMode::WORLD_RELATIVE);
selector.setWorldToHeadPose(0, worldToHead);
+ selector.setScreenStable(0, true);
selector.calculate(0);
EXPECT_EQ(HeadTrackingMode::WORLD_RELATIVE, selector.getActualMode());
EXPECT_EQ(selector.getHeadToStagePose(), worldToHead.inverse());
@@ -69,14 +70,46 @@
ModeSelector selector(options);
selector.setScreenToStagePose(screenToStage);
-
selector.setDesiredMode(HeadTrackingMode::WORLD_RELATIVE);
selector.setWorldToHeadPose(0, worldToHead);
+ selector.setScreenStable(0, true);
selector.calculate(0);
EXPECT_EQ(HeadTrackingMode::WORLD_RELATIVE, selector.getActualMode());
EXPECT_EQ(selector.getHeadToStagePose(), worldToHead.inverse() * screenToStage);
}
+TEST(ModeSelector, WorldRelativeUnstable) {
+ const Pose3f worldToHead({1, 2, 3}, Quaternionf::UnitRandom());
+ const Pose3f screenToStage({4, 5, 6}, Quaternionf::UnitRandom());
+
+ ModeSelector::Options options{.freshnessTimeout = 100};
+ ModeSelector selector(options);
+
+ selector.setScreenToStagePose(screenToStage);
+ selector.setDesiredMode(HeadTrackingMode::WORLD_RELATIVE);
+ selector.setWorldToHeadPose(0, worldToHead);
+ selector.setScreenStable(0, false);
+ selector.calculate(10);
+ EXPECT_EQ(HeadTrackingMode::STATIC, selector.getActualMode());
+ EXPECT_EQ(selector.getHeadToStagePose(), screenToStage);
+}
+
+TEST(ModeSelector, WorldRelativeStableStale) {
+ const Pose3f worldToHead({1, 2, 3}, Quaternionf::UnitRandom());
+ const Pose3f screenToStage({4, 5, 6}, Quaternionf::UnitRandom());
+
+ ModeSelector::Options options{.freshnessTimeout = 100};
+ ModeSelector selector(options);
+
+ selector.setScreenToStagePose(screenToStage);
+ selector.setDesiredMode(HeadTrackingMode::WORLD_RELATIVE);
+ selector.setWorldToHeadPose(100, worldToHead);
+ selector.setScreenStable(0, true);
+ selector.calculate(101);
+ EXPECT_EQ(HeadTrackingMode::STATIC, selector.getActualMode());
+ EXPECT_EQ(selector.getHeadToStagePose(), screenToStage);
+}
+
TEST(ModeSelector, WorldRelativeStale) {
const Pose3f worldToHead({1, 2, 3}, Quaternionf::UnitRandom());
const Pose3f screenToStage({4, 5, 6}, Quaternionf::UnitRandom());
@@ -85,7 +118,6 @@
ModeSelector selector(options);
selector.setScreenToStagePose(screenToStage);
-
selector.setDesiredMode(HeadTrackingMode::WORLD_RELATIVE);
selector.setWorldToHeadPose(0, worldToHead);
selector.calculate(101);
@@ -101,7 +133,6 @@
ModeSelector selector(options);
selector.setScreenToStagePose(screenToStage);
-
selector.setDesiredMode(HeadTrackingMode::SCREEN_RELATIVE);
selector.setScreenToHeadPose(0, screenToHead);
selector.calculate(0);
@@ -118,10 +149,10 @@
ModeSelector selector(options);
selector.setScreenToStagePose(screenToStage);
-
selector.setDesiredMode(HeadTrackingMode::SCREEN_RELATIVE);
selector.setScreenToHeadPose(0, screenToHead);
selector.setWorldToHeadPose(50, worldToHead);
+ selector.setScreenStable(50, true);
selector.calculate(101);
EXPECT_EQ(HeadTrackingMode::WORLD_RELATIVE, selector.getActualMode());
EXPECT_EQ(selector.getHeadToStagePose(), worldToHead.inverse() * screenToStage);
@@ -139,6 +170,7 @@
selector.setDesiredMode(HeadTrackingMode::SCREEN_RELATIVE);
selector.setScreenToHeadPose(50, std::nullopt);
selector.setWorldToHeadPose(50, worldToHead);
+ selector.setScreenStable(50, true);
selector.calculate(101);
EXPECT_EQ(HeadTrackingMode::WORLD_RELATIVE, selector.getActualMode());
EXPECT_EQ(selector.getHeadToStagePose(), worldToHead.inverse() * screenToStage);
diff --git a/media/libheadtracking/ModeSelector.cpp b/media/libheadtracking/ModeSelector.cpp
index 16e1712..cb3a27f 100644
--- a/media/libheadtracking/ModeSelector.cpp
+++ b/media/libheadtracking/ModeSelector.cpp
@@ -41,11 +41,18 @@
mWorldToHeadTimestamp = timestamp;
}
+void ModeSelector::setScreenStable(int64_t timestamp, bool stable) {
+ mScreenStable = stable;
+ mScreenStableTimestamp = timestamp;
+}
+
void ModeSelector::calculateActualMode(int64_t timestamp) {
bool isValidScreenToHead = mScreenToHead.has_value() &&
timestamp - mScreenToHeadTimestamp < mOptions.freshnessTimeout;
bool isValidWorldToHead = mWorldToHead.has_value() &&
timestamp - mWorldToHeadTimestamp < mOptions.freshnessTimeout;
+ bool isValidScreenStable = mScreenStable.has_value() &&
+ timestamp - mScreenStableTimestamp < mOptions.freshnessTimeout;
HeadTrackingMode mode = mDesiredMode;
@@ -58,7 +65,7 @@
// Optional downgrade from world-relative to static.
if (mode == HeadTrackingMode::WORLD_RELATIVE) {
- if (!isValidWorldToHead) {
+ if (!isValidWorldToHead || !isValidScreenStable || !mScreenStable.value()) {
mode = HeadTrackingMode::STATIC;
}
}
diff --git a/media/libheadtracking/ModeSelector.h b/media/libheadtracking/ModeSelector.h
index 17a5142..e537040 100644
--- a/media/libheadtracking/ModeSelector.h
+++ b/media/libheadtracking/ModeSelector.h
@@ -56,6 +56,7 @@
* from screen-relative to world-relative.
* - When we cannot get a fresh estimate of the world-to-head pose, we will fall back from
* world-relative to static.
+ * - In world-relative mode, if the screen is unstable, we will fall back to static.
*
* All the timestamps used here are of arbitrary units and origin. They just need to be consistent
* between all the calls and with the Options provided for determining freshness and rate limiting.
@@ -92,6 +93,12 @@
void setWorldToHeadPose(int64_t timestamp, const Pose3f& worldToHead);
/**
+ * Set whether the screen is considered stable.
+ * The timestamp needs to reflect how fresh the sample is.
+ */
+ void setScreenStable(int64_t timestamp, bool stable);
+
+ /**
* Process all the previous inputs and update the outputs.
*/
void calculate(int64_t timestamp);
@@ -116,6 +123,8 @@
int64_t mScreenToHeadTimestamp;
std::optional<Pose3f> mWorldToHead;
int64_t mWorldToHeadTimestamp;
+ std::optional<bool> mScreenStable;
+ int64_t mScreenStableTimestamp;
HeadTrackingMode mActualMode;
Pose3f mHeadToStage;
diff --git a/media/libheadtracking/StillnessDetector-test.cpp b/media/libheadtracking/StillnessDetector-test.cpp
new file mode 100644
index 0000000..a53ba8c
--- /dev/null
+++ b/media/libheadtracking/StillnessDetector-test.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "QuaternionUtil.h"
+#include "StillnessDetector.h"
+#include "TestUtil.h"
+
+namespace android {
+namespace media {
+namespace {
+
+using Eigen::Quaternionf;
+using Eigen::Vector3f;
+using Options = StillnessDetector::Options;
+
+TEST(StillnessDetectorTest, Still) {
+ StillnessDetector detector(Options{
+ .windowDuration = 1000, .translationalThreshold = 1, .rotationalThreshold = 0.05});
+
+ const Pose3f baseline(Vector3f{1, 2, 3}, Quaternionf::UnitRandom());
+ const Pose3f withinThreshold =
+ baseline * Pose3f(Vector3f(0.3, -0.3, 0), rotateX(0.01) * rotateY(-0.01));
+
+ EXPECT_FALSE(detector.calculate(0));
+ detector.setInput(0, baseline);
+ EXPECT_FALSE(detector.calculate(0));
+ detector.setInput(300, withinThreshold);
+ EXPECT_FALSE(detector.calculate(300));
+ detector.setInput(600, baseline);
+ EXPECT_FALSE(detector.calculate(600));
+ detector.setInput(999, withinThreshold);
+ EXPECT_FALSE(detector.calculate(999));
+ detector.setInput(1000, baseline);
+ EXPECT_TRUE(detector.calculate(1000));
+}
+
+TEST(StillnessDetectorTest, ZeroDuration) {
+ StillnessDetector detector(Options{.windowDuration = 0});
+ EXPECT_TRUE(detector.calculate(0));
+ EXPECT_TRUE(detector.calculate(1000));
+}
+
+TEST(StillnessDetectorTest, NotStillTranslation) {
+ StillnessDetector detector(Options{
+ .windowDuration = 1000, .translationalThreshold = 1, .rotationalThreshold = 0.05});
+
+ const Pose3f baseline(Vector3f{1, 2, 3}, Quaternionf::UnitRandom());
+ const Pose3f withinThreshold =
+ baseline * Pose3f(Vector3f(0.3, -0.3, 0), rotateX(0.01) * rotateY(-0.01));
+ const Pose3f outsideThreshold = baseline * Pose3f(Vector3f(1, 1, 0));
+
+ EXPECT_FALSE(detector.calculate(0));
+ detector.setInput(0, baseline);
+ EXPECT_FALSE(detector.calculate(0));
+ detector.setInput(300, outsideThreshold);
+ EXPECT_FALSE(detector.calculate(300));
+ detector.setInput(600, baseline);
+ EXPECT_FALSE(detector.calculate(600));
+ detector.setInput(900, withinThreshold);
+ EXPECT_FALSE(detector.calculate(900));
+ detector.setInput(1299, baseline);
+ EXPECT_FALSE(detector.calculate(1299));
+ EXPECT_TRUE(detector.calculate(1300));
+}
+
+TEST(StillnessDetectorTest, NotStillRotation) {
+ StillnessDetector detector(Options{
+ .windowDuration = 1000, .translationalThreshold = 1, .rotationalThreshold = 0.05});
+
+ const Pose3f baseline(Vector3f{1, 2, 3}, Quaternionf::UnitRandom());
+ const Pose3f withinThreshold =
+ baseline * Pose3f(Vector3f(0.3, -0.3, 0), rotateX(0.01) * rotateY(-0.01));
+ const Pose3f outsideThreshold = baseline * Pose3f(rotateZ(0.08));
+ EXPECT_FALSE(detector.calculate(0));
+ detector.setInput(0, baseline);
+ EXPECT_FALSE(detector.calculate(0));
+ detector.setInput(300, outsideThreshold);
+ EXPECT_FALSE(detector.calculate(300));
+ detector.setInput(600, baseline);
+ EXPECT_FALSE(detector.calculate(600));
+ detector.setInput(900, withinThreshold);
+ EXPECT_FALSE(detector.calculate(900));
+ detector.setInput(1299, baseline);
+ EXPECT_FALSE(detector.calculate(1299));
+ EXPECT_TRUE(detector.calculate(1300));
+}
+
+TEST(StillnessDetectorTest, Reset) {
+ StillnessDetector detector(Options{
+ .windowDuration = 1000, .translationalThreshold = 1, .rotationalThreshold = 0.05});
+
+ const Pose3f baseline(Vector3f{1, 2, 3}, Quaternionf::UnitRandom());
+ const Pose3f withinThreshold =
+ baseline * Pose3f(Vector3f(0.3, -0.3, 0), rotateX(0.01) * rotateY(-0.01));
+ EXPECT_FALSE(detector.calculate(0));
+ detector.setInput(0, baseline);
+ EXPECT_FALSE(detector.calculate(0));
+ detector.reset();
+ detector.setInput(600, baseline);
+ EXPECT_FALSE(detector.calculate(600));
+ detector.setInput(900, withinThreshold);
+ EXPECT_FALSE(detector.calculate(900));
+ detector.setInput(1200, baseline);
+ EXPECT_FALSE(detector.calculate(1200));
+ detector.setInput(1599, withinThreshold);
+ EXPECT_FALSE(detector.calculate(1599));
+ detector.setInput(1600, baseline);
+ EXPECT_TRUE(detector.calculate(1600));
+}
+
+} // namespace
+} // namespace media
+} // namespace android
diff --git a/media/libheadtracking/StillnessDetector.cpp b/media/libheadtracking/StillnessDetector.cpp
new file mode 100644
index 0000000..832351d
--- /dev/null
+++ b/media/libheadtracking/StillnessDetector.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#include "StillnessDetector.h"
+
+namespace android {
+namespace media {
+
+StillnessDetector::StillnessDetector(const Options& options) : mOptions(options) {}
+
+void StillnessDetector::reset() {
+ mFifo.clear();
+ mWindowFull = false;
+}
+
+void StillnessDetector::setInput(int64_t timestamp, const Pose3f& input) {
+ mFifo.push_back(TimestampedPose{timestamp, input});
+ discardOld(timestamp);
+}
+
+bool StillnessDetector::calculate(int64_t timestamp) {
+ discardOld(timestamp);
+
+ // If the window has not been full, we don't consider ourselves still.
+ if (!mWindowFull) {
+ return false;
+ }
+
+ // An empty FIFO and window full is considered still (this will happen in the unlikely case when
+ // the window duration is shorter than the gap between samples).
+ if (mFifo.empty()) {
+ return true;
+ }
+
+ // Otherwise, check whether all the poses remaining in the queue are in the proximity of the new
+ // one.
+ for (auto iter = mFifo.begin(); iter != mFifo.end() - 1; ++iter) {
+ const auto& event = *iter;
+ if (!areNear(event.pose, mFifo.back().pose)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void StillnessDetector::discardOld(int64_t timestamp) {
+ // Handle the special case of the window duration being zero (always considered full).
+ if (mOptions.windowDuration == 0) {
+ mFifo.clear();
+ mWindowFull = true;
+ }
+
+ // Remove any events from the queue that are older than the window. If there were any such
+ // events we consider the window full.
+ const int64_t windowStart = timestamp - mOptions.windowDuration;
+ while (!mFifo.empty() && mFifo.front().timestamp <= windowStart) {
+ mWindowFull = true;
+ mFifo.pop_front();
+ }
+}
+
+bool StillnessDetector::areNear(const Pose3f& pose1, const Pose3f& pose2) const {
+ // Check translation. We use the L1 norm to reduce computational load on expense of accuracy.
+ // The L1 norm is an upper bound for the actual (L2) norm, so this approach will err on the side
+ // of "not near".
+ if ((pose1.translation() - pose2.translation()).lpNorm<1>() >=
+ mOptions.translationalThreshold) {
+ return false;
+ }
+
+ // Check orientation. We use the L1 norm of the imaginary components of the quaternion to reduce
+ // computational load on expense of accuracy. For small angles, those components are approx.
+ // equal to the angle of rotation and so the norm is approx. the total angle of rotation. The
+ // L1 norm is an upper bound, so this approach will err on the side of "not near".
+ if ((pose1.rotation().vec() - pose2.rotation().vec()).lpNorm<1>() >=
+ mOptions.rotationalThreshold) {
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace media
+} // namespace android
diff --git a/media/libheadtracking/StillnessDetector.h b/media/libheadtracking/StillnessDetector.h
new file mode 100644
index 0000000..fd26aa9
--- /dev/null
+++ b/media/libheadtracking/StillnessDetector.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+#pragma once
+
+#include <deque>
+
+#include <media/Pose.h>
+
+namespace android {
+namespace media {
+
+/**
+ * Given a stream of poses, determines if the pose is stable ("still").
+ * Stillness is defined as all poses in the recent history ("window") being near the most recent
+ * sample.
+ *
+ * Typical usage:
+ *
+ * StillnessDetector detector(StilnessDetector::Options{...});
+ *
+ * while (...) {
+ * detector.setInput(timestamp, pose);
+ * bool still = detector.calculate(timestamp);
+ * }
+ *
+ * The stream is considered not still until a sufficient number of samples has been provided for an
+ * initial fill-up of the window. In the special case of the window size being 0, this is not
+ * required and the state is considered always "still". The reset() method can be used to empty the
+ * window again and get back to this initial state.
+ */
+class StillnessDetector {
+ public:
+ /**
+ * Configuration options for the detector.
+ */
+ struct Options {
+ /**
+ * How long is the window, in ticks. The special value of 0 indicates that the stream is
+ * always considered still.
+ */
+ int64_t windowDuration;
+ /**
+ * How much of a translational deviation from the target (in meters) is considered motion.
+ * This is an approximate quantity - the actual threshold might be a little different as we
+ * trade-off accuracy with computational efficiency.
+ */
+ float translationalThreshold;
+ /**
+ * How much of a rotational deviation from the target (in radians) is considered motion.
+ * This is an approximate quantity - the actual threshold might be a little different as we
+ * trade-off accuracy with computational efficiency.
+ */
+ float rotationalThreshold;
+ };
+
+ /** Ctor. */
+ explicit StillnessDetector(const Options& options);
+
+ /** Clear the window. */
+ void reset();
+ /** Push a new sample. */
+ void setInput(int64_t timestamp, const Pose3f& input);
+ /** Calculate whether the stream is still at the given timestamp. */
+ bool calculate(int64_t timestamp);
+
+ private:
+ struct TimestampedPose {
+ int64_t timestamp;
+ Pose3f pose;
+ };
+
+ const Options mOptions;
+ std::deque<TimestampedPose> mFifo;
+ bool mWindowFull = false;
+
+ bool areNear(const Pose3f& pose1, const Pose3f& pose2) const;
+ void discardOld(int64_t timestamp);
+};
+
+} // namespace media
+} // namespace android
diff --git a/media/libheadtracking/include/media/HeadTrackingProcessor.h b/media/libheadtracking/include/media/HeadTrackingProcessor.h
index 9fea273..2af560e 100644
--- a/media/libheadtracking/include/media/HeadTrackingProcessor.h
+++ b/media/libheadtracking/include/media/HeadTrackingProcessor.h
@@ -42,6 +42,12 @@
float rotationalDriftTimeConstant = std::numeric_limits<float>::infinity();
int64_t freshnessTimeout = std::numeric_limits<int64_t>::max();
float predictionDuration = 0;
+ int64_t autoRecenterWindowDuration = std::numeric_limits<int64_t>::max();
+ float autoRecenterTranslationalThreshold = std::numeric_limits<float>::infinity();
+ float autoRecenterRotationalThreshold = std::numeric_limits<float>::infinity();
+ int64_t screenStillnessWindowDuration = 0;
+ float screenStillnessTranslationalThreshold = std::numeric_limits<float>::infinity();
+ float screenStillnessRotationalThreshold = std::numeric_limits<float>::infinity();
};
/** Sets the desired head-tracking mode. */
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index ca7ffdb..b748f9d 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -2413,6 +2413,7 @@
if (i == size - 1 && i != 0) {
mEffects[i - 1]->configure();
mEffects[i - 1]->setOutBuffer(mOutBuffer);
+ mEffects[i - 1]->updateAccessMode(); // reconfig if neeeded.
}
}
mEffects.removeAt(i);
@@ -2422,6 +2423,7 @@
if (i == 0 && size > 1) {
mEffects[0]->configure();
mEffects[0]->setInBuffer(mInBuffer);
+ mEffects[0]->updateAccessMode(); // reconfig if neeeded.
}
ALOGV("removeEffect_l() effect %p, removed from chain %p at rank %zu", effect.get(),
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index 93118b8..45dd258 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -575,6 +575,12 @@
// create a special playback track to render to playback thread.
// this track is given the same buffer as the PatchRecord buffer
+
+ // Default behaviour is to start as soon as possible to have the lowest possible latency even if
+ // it might glitch.
+ // Disable this behavior for FM Tuner source if no fast capture/mixer available.
+ const bool isFmBridge = mAudioPatch.sources[0].ext.device.type == AUDIO_DEVICE_IN_FM_TUNER;
+ const size_t frameCountToBeReady = isFmBridge && !usePassthruPatchRecord ? frameCount / 4 : 1;
sp<PlaybackThread::PatchTrack> tempPatchTrack = new PlaybackThread::PatchTrack(
mPlayback.thread().get(),
streamType,
@@ -584,7 +590,9 @@
frameCount,
tempRecordTrack->buffer(),
tempRecordTrack->bufferSize(),
- outputFlags);
+ outputFlags,
+ {} /*timeout*/,
+ frameCountToBeReady);
status = mPlayback.checkTrack(tempPatchTrack.get());
if (status != NO_ERROR) {
return status;
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 33b455f..f6f3b9a 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -392,6 +392,18 @@
* @return NO_ERROR if an output was closed, INVALID_OPERATION or BAD_VALUE otherwise
*/
virtual status_t releaseSpatializerOutput(audio_io_handle_t output) = 0;
+
+ /**
+ * Query how the direct playback is currently supported on the device.
+ * @param attr audio attributes describing the playback use case
+ * @param config audio configuration for the playback
+ * @param directMode out: a set of flags describing how the direct playback is currently
+ * supported on the device
+ * @return NO_ERROR in case of success, DEAD_OBJECT, NO_INIT, BAD_VALUE, PERMISSION_DENIED
+ * in case of error.
+ */
+ virtual audio_direct_mode_t getDirectPlaybackSupport(const audio_attributes_t *attr,
+ const audio_config_t *config) = 0;
};
diff --git a/services/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h
index c565926..2ebb7df 100644
--- a/services/audiopolicy/engine/config/include/EngineConfig.h
+++ b/services/audiopolicy/engine/config/include/EngineConfig.h
@@ -70,7 +70,7 @@
using ProductStrategies = std::vector<ProductStrategy>;
-using ValuePair = std::pair<uint32_t, std::string>;
+using ValuePair = std::tuple<uint64_t, uint32_t, std::string>;
using ValuePairs = std::vector<ValuePair>;
struct CriterionType
diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp
index 81e803f..6f560d5 100644
--- a/services/audiopolicy/engine/config/src/EngineConfig.cpp
+++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp
@@ -80,6 +80,7 @@
struct Attributes {
static constexpr const char *literal = "literal";
static constexpr const char *numerical = "numerical";
+ static constexpr const char *androidType = "android_type";
};
static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root,
@@ -349,7 +350,16 @@
ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::literal);
return BAD_VALUE;
}
- uint32_t numerical = 0;
+ uint32_t androidType = 0;
+ std::string androidTypeliteral = getXmlAttribute(child, Attributes::androidType);
+ if (!androidTypeliteral.empty()) {
+ ALOGV("%s: androidType %s", __FUNCTION__, androidTypeliteral.c_str());
+ if (!convertTo(androidTypeliteral, androidType)) {
+ ALOGE("%s: : Invalid typeset value(%s)", __FUNCTION__, androidTypeliteral.c_str());
+ return BAD_VALUE;
+ }
+ }
+ uint64_t numerical = 0;
std::string numericalTag = getXmlAttribute(child, Attributes::numerical);
if (numericalTag.empty()) {
ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::literal);
@@ -359,7 +369,7 @@
ALOGE("%s: : Invalid value(%s)", __FUNCTION__, numericalTag.c_str());
return BAD_VALUE;
}
- values.push_back({numerical, literal});
+ values.push_back({numerical, androidType, literal});
return NO_ERROR;
}
diff --git a/services/audiopolicy/engineconfigurable/interface/AudioPolicyPluginInterface.h b/services/audiopolicy/engineconfigurable/interface/AudioPolicyPluginInterface.h
index 1fc2264..9fd8b8e 100644
--- a/services/audiopolicy/engineconfigurable/interface/AudioPolicyPluginInterface.h
+++ b/services/audiopolicy/engineconfigurable/interface/AudioPolicyPluginInterface.h
@@ -77,12 +77,12 @@
* Set the input device to be used by an input source.
*
* @param[in] inputSource: name of the input source for which the device to use has to be set
- * @param[in] devices; mask of devices to be used for the given input source.
+ * @param[in] devices: mask of devices to be used for the given input source.
*
* @return true if the devices were set correclty for this input source, false otherwise.
*/
virtual bool setDeviceForInputSource(const audio_source_t &inputSource,
- audio_devices_t device) = 0;
+ uint64_t device) = 0;
virtual void setDeviceAddressForProductStrategy(product_strategy_t strategy,
const std::string &address) = 0;
@@ -91,12 +91,12 @@
* Set the device to be used by a product strategy.
*
* @param[in] strategy: name of the product strategy for which the device to use has to be set
- * @param[in] devices; mask of devices to be used for the given strategy.
+ * @param[in] devices: mask of devices to be used for the given strategy.
*
* @return true if the devices were set correclty for this strategy, false otherwise.
*/
virtual bool setDeviceTypesForProductStrategy(product_strategy_t strategy,
- audio_devices_t devices) = 0;
+ uint64_t devices) = 0;
virtual product_strategy_t getProductStrategyByName(const std::string &address) = 0;
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-CommonTypes.xml.in b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-CommonTypes.xml.in
index 2e9f37e..2c4c7b5 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-CommonTypes.xml.in
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-CommonTypes.xml.in
@@ -10,8 +10,8 @@
<!--#################### GLOBAL COMPONENTS END ####################-->
<!-- Automatically filled from audio-base.h file -->
- <ComponentType Name="OutputDevicesMask" Description="32th bit is not allowed as dedicated for input devices detection">
- <BitParameterBlock Name="mask" Size="32">
+ <ComponentType Name="OutputDevicesMask" Description="64bit representation of devices">
+ <BitParameterBlock Name="mask" Size="64">
</BitParameterBlock>
</ComponentType>
@@ -19,8 +19,8 @@
profile. It must match with the Input device enum parameter.
-->
<!-- Automatically filled from audio-base.h file -->
- <ComponentType Name="InputDevicesMask">
- <BitParameterBlock Name="mask" Size="32">
+ <ComponentType Name="InputDevicesMask" Description="64bit representation of devices">
+ <BitParameterBlock Name="mask" Size="64">
</BitParameterBlock>
</ComponentType>
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/InputSource.cpp b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/InputSource.cpp
index f8a6fc0..df4e3e9 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/InputSource.cpp
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/InputSource.cpp
@@ -45,7 +45,7 @@
bool InputSource::sendToHW(string & /*error*/)
{
- audio_devices_t applicableInputDevice;
+ uint64_t applicableInputDevice;
blackboardRead(&applicableInputDevice, sizeof(applicableInputDevice));
return mPolicyPluginInterface->setDeviceForInputSource(mId, applicableInputDevice);
}
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/ProductStrategy.h b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/ProductStrategy.h
index 6c8eb65..e65946e 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/ProductStrategy.h
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/ProductStrategy.h
@@ -32,7 +32,7 @@
struct Device
{
- audio_devices_t applicableDevice; /**< applicable device for this strategy. */
+ uint64_t applicableDevice; /**< applicable device for this strategy. */
char deviceAddress[mMaxStringSize]; /**< device address associated with this strategy. */
} __attribute__((packed));
diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp
index 9a61a05..3d74920 100644
--- a/services/audiopolicy/engineconfigurable/src/Engine.cpp
+++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp
@@ -36,6 +36,8 @@
#include <media/TypeConverter.h>
+#include <cinttypes>
+
using std::string;
using std::map;
@@ -166,16 +168,13 @@
status_t Engine::setDeviceConnectionState(const sp<DeviceDescriptor> device,
audio_policy_dev_state_t state)
{
- mPolicyParameterMgr->setDeviceConnectionState(
- device->type(), device->address().c_str(), state);
+ mPolicyParameterMgr->setDeviceConnectionState(device->type(), device->address(), state);
if (audio_is_output_device(device->type())) {
- // FIXME: Use DeviceTypeSet when the interface is ready
return mPolicyParameterMgr->setAvailableOutputDevices(
- deviceTypesToBitMask(getApmObserver()->getAvailableOutputDevices().types()));
+ getApmObserver()->getAvailableOutputDevices().types());
} else if (audio_is_input_device(device->type())) {
- // FIXME: Use DeviceTypeSet when the interface is ready
return mPolicyParameterMgr->setAvailableInputDevices(
- deviceTypesToBitMask(getApmObserver()->getAvailableInputDevices().types()));
+ getApmObserver()->getAvailableInputDevices().types());
}
return EngineBase::setDeviceConnectionState(device, state);
}
@@ -374,17 +373,28 @@
getProductStrategies().at(strategy)->setDeviceAddress(address);
}
-bool Engine::setDeviceTypesForProductStrategy(product_strategy_t strategy, audio_devices_t devices)
+bool Engine::setDeviceTypesForProductStrategy(product_strategy_t strategy, uint64_t devices)
{
if (getProductStrategies().find(strategy) == getProductStrategies().end()) {
- ALOGE("%s: set device %d on invalid strategy %d", __FUNCTION__, devices, strategy);
+ ALOGE("%s: set device %" PRId64 " on invalid strategy %d", __FUNCTION__, devices, strategy);
return false;
}
- // FIXME: stop using deviceTypesFromBitMask when the interface is ready
- getProductStrategies().at(strategy)->setDeviceTypes(deviceTypesFromBitMask(devices));
+ // Here device matches the criterion value, need to rebuitd android device types;
+ DeviceTypeSet types =
+ mPolicyParameterMgr->convertDeviceCriterionValueToDeviceTypes(devices, true /*isOut*/);
+ getProductStrategies().at(strategy)->setDeviceTypes(types);
return true;
}
+bool Engine::setDeviceForInputSource(const audio_source_t &inputSource, uint64_t device)
+{
+ DeviceTypeSet types = mPolicyParameterMgr->convertDeviceCriterionValueToDeviceTypes(
+ device, false /*isOut*/);
+ ALOG_ASSERT(types.size() <= 1, "one input device expected at most");
+ audio_devices_t deviceType = types.empty() ? AUDIO_DEVICE_IN_DEFAULT : *types.begin();
+ return setPropertyForKey<audio_devices_t, audio_source_t>(deviceType, inputSource);
+}
+
template <>
EngineInterface *Engine::queryInterface()
{
diff --git a/services/audiopolicy/engineconfigurable/src/Engine.h b/services/audiopolicy/engineconfigurable/src/Engine.h
index f665da5..4b559f0 100644
--- a/services/audiopolicy/engineconfigurable/src/Engine.h
+++ b/services/audiopolicy/engineconfigurable/src/Engine.h
@@ -82,15 +82,12 @@
bool setVolumeProfileForStream(const audio_stream_type_t &stream,
const audio_stream_type_t &volumeProfile) override;
- bool setDeviceForInputSource(const audio_source_t &inputSource, audio_devices_t device) override
- {
- return setPropertyForKey<audio_devices_t, audio_source_t>(device, inputSource);
- }
+ bool setDeviceForInputSource(const audio_source_t &inputSource, uint64_t device) override;
+
void setDeviceAddressForProductStrategy(product_strategy_t strategy,
const std::string &address) override;
- bool setDeviceTypesForProductStrategy(product_strategy_t strategy,
- audio_devices_t devices) override;
+ bool setDeviceTypesForProductStrategy(product_strategy_t strategy, uint64_t devices) override;
product_strategy_t getProductStrategyByName(const std::string &name) override
{
diff --git a/services/audiopolicy/engineconfigurable/src/InputSource.cpp b/services/audiopolicy/engineconfigurable/src/InputSource.cpp
index f4645e6..5779c00 100644
--- a/services/audiopolicy/engineconfigurable/src/InputSource.cpp
+++ b/services/audiopolicy/engineconfigurable/src/InputSource.cpp
@@ -46,12 +46,6 @@
template <>
status_t Element<audio_source_t>::set(audio_devices_t devices)
{
- if (devices == AUDIO_DEVICE_NONE) {
- // Reset
- mApplicableDevices = devices;
- return NO_ERROR;
- }
- devices = static_cast<audio_devices_t>(devices | AUDIO_DEVICE_BIT_IN);
if (!audio_is_input_device(devices)) {
ALOGE("%s: trying to set an invalid device 0x%X for input source %s",
__FUNCTION__, devices, getName().c_str());
diff --git a/services/audiopolicy/engineconfigurable/tools/buildCommonTypesStructureFile.py b/services/audiopolicy/engineconfigurable/tools/buildCommonTypesStructureFile.py
index 43b3dd2..86ac76f 100755
--- a/services/audiopolicy/engineconfigurable/tools/buildCommonTypesStructureFile.py
+++ b/services/audiopolicy/engineconfigurable/tools/buildCommonTypesStructureFile.py
@@ -55,7 +55,7 @@
while i < decimal:
i = i << 1
pos = pos + 1
- if pos == 32:
+ if pos == 64:
return -1
# TODO: b/168065706. This is just to fix the build. That the problem of devices with
@@ -132,6 +132,9 @@
logging.info("Checking Android Header file {}".format(androidaudiobaseheaderFile))
+ multi_bit_output_device_shift = 32
+ multi_bit_input_device_shift = 32
+
for line_number, line in enumerate(androidaudiobaseheaderFile):
match = criteria_pattern.match(line)
if match:
@@ -143,16 +146,36 @@
component_type_numerical_value = match.groupdict()['values']
- # for AUDIO_DEVICE_IN: need to remove sign bit / rename default to stub
+ # for AUDIO_DEVICE_IN: rename default to stub
if component_type_name == "InputDevicesMask":
- component_type_numerical_value = str(int(component_type_numerical_value, 0) & ~2147483648)
+ component_type_numerical_value = str(int(component_type_numerical_value, 0))
if component_type_literal == "default":
component_type_literal = "stub"
+ string_int = int(component_type_numerical_value, 0)
+ num_bits = bin(string_int).count("1")
+ if num_bits > 1:
+ logging.info("The value {} is for criterion {} binary rep {} has {} bits sets"
+ .format(component_type_numerical_value, component_type_name, bin(string_int), num_bits))
+ string_int = 2**multi_bit_input_device_shift
+ logging.info("new val assigned is {} {}" .format(string_int, bin(string_int)))
+ multi_bit_input_device_shift += 1
+ component_type_numerical_value = str(string_int)
+
if component_type_name == "OutputDevicesMask":
if component_type_literal == "default":
component_type_literal = "stub"
+ string_int = int(component_type_numerical_value, 0)
+ num_bits = bin(string_int).count("1")
+ if num_bits > 1:
+ logging.info("The value {} is for criterion {} binary rep {} has {} bits sets"
+ .format(component_type_numerical_value, component_type_name, bin(string_int), num_bits))
+ string_int = 2**multi_bit_output_device_shift
+ logging.info("new val assigned is {} {}" .format(string_int, bin(string_int)))
+ multi_bit_output_device_shift += 1
+ component_type_numerical_value = str(string_int)
+
# Remove duplicated numerical values
if int(component_type_numerical_value, 0) in all_component_types[component_type_name].values():
logging.info("The value {}:{} is duplicated for criterion {}, KEEPING LATEST".format(component_type_numerical_value, component_type_literal, component_type_name))
diff --git a/services/audiopolicy/engineconfigurable/tools/buildPolicyCriterionTypes.py b/services/audiopolicy/engineconfigurable/tools/buildPolicyCriterionTypes.py
index 76c35c1..a15a6ba 100755
--- a/services/audiopolicy/engineconfigurable/tools/buildPolicyCriterionTypes.py
+++ b/services/audiopolicy/engineconfigurable/tools/buildPolicyCriterionTypes.py
@@ -85,6 +85,9 @@
return argparser.parse_args()
+output_devices_type_value = {}
+input_devices_type_value = {}
+
def generateXmlCriterionTypesFile(criterionTypes, addressCriteria, criterionTypesFile, outputFile):
logging.info("Importing criterionTypesFile {}".format(criterionTypesFile))
@@ -102,6 +105,11 @@
value_node.set('numerical', str(value))
value_node.set('literal', key)
+ if criterion_type.get('name') == "OutputDevicesMaskType":
+ value_node.set('android_type', output_devices_type_value[key])
+ if criterion_type.get('name') == "InputDevicesMaskType":
+ value_node.set('android_type', input_devices_type_value[key])
+
if addressCriteria:
for criterion_name, values_list in addressCriteria.items():
for criterion_type in criterion_types_root.findall('criterion_type'):
@@ -200,10 +208,8 @@
#
ignored_values = ['CNT', 'MAX', 'ALL', 'NONE']
- #
- # Reaching 32 bit limit for inclusive criterion out devices: removing
- #
- ignored_output_device_values = ['BleSpeaker', 'BleHeadset']
+ multi_bit_outputdevice_shift = 32
+ multi_bit_inputdevice_shift = 32
criteria_pattern = re.compile(
r"\s*V\((?P<type>(?:"+'|'.join(criterion_mapping_table.keys()) + "))_" \
@@ -223,28 +229,59 @@
''.join((w.capitalize() for w in match.groupdict()['literal'].split('_')))
criterion_numerical_value = match.groupdict()['values']
- # for AUDIO_DEVICE_IN: need to remove sign bit / rename default to stub
+ # for AUDIO_DEVICE_IN: rename default to stub
if criterion_name == "InputDevicesMaskType":
if criterion_literal == "Default":
criterion_numerical_value = str(int("0x40000000", 0))
+ input_devices_type_value[criterion_literal] = "0xC0000000"
else:
try:
string_int = int(criterion_numerical_value, 0)
+ # Append AUDIO_DEVICE_IN for android type tag
+ input_devices_type_value[criterion_literal] = hex(string_int | 2147483648)
+
+ num_bits = bin(string_int).count("1")
+ if num_bits > 1:
+ logging.info("The value {}:{} is for criterion {} binary rep {} has {} bits sets"
+ .format(criterion_numerical_value, criterion_literal, criterion_name, bin(string_int), num_bits))
+ string_int = 2**multi_bit_inputdevice_shift
+ logging.info("new val assigned is {} {}" .format(string_int, bin(string_int)))
+ multi_bit_inputdevice_shift += 1
+ criterion_numerical_value = str(string_int)
+
except ValueError:
# Handle the exception
logging.info("value {}:{} for criterion {} is not a number, ignoring"
.format(criterion_numerical_value, criterion_literal, criterion_name))
continue
- criterion_numerical_value = str(int(criterion_numerical_value, 0) & ~2147483648)
if criterion_name == "OutputDevicesMaskType":
if criterion_literal == "Default":
criterion_numerical_value = str(int("0x40000000", 0))
- if criterion_literal in ignored_output_device_values:
- logging.info("OutputDevicesMaskType skipping {}".format(criterion_literal))
- continue
+ output_devices_type_value[criterion_literal] = "0x40000000"
+ else:
+ try:
+ string_int = int(criterion_numerical_value, 0)
+ output_devices_type_value[criterion_literal] = criterion_numerical_value
+
+ num_bits = bin(string_int).count("1")
+ if num_bits > 1:
+ logging.info("The value {}:{} is for criterion {} binary rep {} has {} bits sets"
+ .format(criterion_numerical_value, criterion_literal, criterion_name, bin(string_int), num_bits))
+ string_int = 2**multi_bit_outputdevice_shift
+ logging.info("new val assigned is {} {}" .format(string_int, bin(string_int)))
+ multi_bit_outputdevice_shift += 1
+ criterion_numerical_value = str(string_int)
+
+ except ValueError:
+ # Handle the exception
+ logging.info("The value {}:{} is for criterion {} is not a number, ignoring"
+ .format(criterion_numerical_value, criterion_literal, criterion_name))
+ continue
+
try:
string_int = int(criterion_numerical_value, 0)
+
except ValueError:
# Handle the exception
logging.info("The value {}:{} is for criterion {} is not a number, ignoring"
diff --git a/services/audiopolicy/engineconfigurable/wrapper/Android.bp b/services/audiopolicy/engineconfigurable/wrapper/Android.bp
index 3e04b68..0ef0b82 100644
--- a/services/audiopolicy/engineconfigurable/wrapper/Android.bp
+++ b/services/audiopolicy/engineconfigurable/wrapper/Android.bp
@@ -19,6 +19,7 @@
header_libs: [
"libbase_headers",
"libaudiopolicycommon",
+ "libaudiofoundation_headers",
],
shared_libs: [
"liblog",
diff --git a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp
index 63990ac..099d55d 100644
--- a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp
+++ b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp
@@ -23,6 +23,7 @@
#include <SelectionCriterionInterface.h>
#include <media/convert.h>
#include <algorithm>
+#include <cutils/bitops.h>
#include <cutils/config_utils.h>
#include <cutils/misc.h>
#include <fstream>
@@ -31,6 +32,7 @@
#include <string>
#include <vector>
#include <stdint.h>
+#include <cinttypes>
#include <cmath>
#include <utils/Log.h>
@@ -124,9 +126,22 @@
for (auto pair : pairs) {
std::string error;
- ALOGV("%s: Adding pair %d,%s for criterionType %s", __FUNCTION__, pair.first,
- pair.second.c_str(), name.c_str());
- criterionType->addValuePair(pair.first, pair.second, error);
+ ALOGV("%s: Adding pair %" PRIu64", %s for criterionType %s", __func__, std::get<0>(pair),
+ std::get<2>(pair).c_str(), name.c_str());
+ criterionType->addValuePair(std::get<0>(pair), std::get<2>(pair), error);
+
+ if (name == gOutputDeviceCriterionName) {
+ ALOGV("%s: Adding mOutputDeviceToCriterionTypeMap %d %" PRIu64" for criterionType %s",
+ __func__, std::get<1>(pair), std::get<0>(pair), name.c_str());
+ audio_devices_t androidType = static_cast<audio_devices_t>(std::get<1>(pair));
+ mOutputDeviceToCriterionTypeMap[androidType] = std::get<0>(pair);
+ }
+ if (name == gInputDeviceCriterionName) {
+ ALOGV("%s: Adding mInputDeviceToCriterionTypeMap %d %" PRIu64" for criterionType %s",
+ __func__, std::get<1>(pair), std::get<0>(pair), name.c_str());
+ audio_devices_t androidType = static_cast<audio_devices_t>(std::get<1>(pair));
+ mInputDeviceToCriterionTypeMap[androidType] = std::get<0>(pair);
+ }
}
ALOG_ASSERT(mPolicyCriteria.find(name) == mPolicyCriteria.end(),
"%s: Criterion %s already added", __FUNCTION__, name.c_str());
@@ -135,7 +150,7 @@
mPolicyCriteria[name] = criterion;
if (not defaultValue.empty()) {
- int numericalValue = 0;
+ uint64_t numericalValue = 0;
if (not criterionType->getNumericalValue(defaultValue.c_str(), numericalValue)) {
ALOGE("%s; trying to apply invalid default literal value (%s)", __FUNCTION__,
defaultValue.c_str());
@@ -263,7 +278,7 @@
}
status_t ParameterManagerWrapper::setDeviceConnectionState(
- audio_devices_t type, const std::string address, audio_policy_dev_state_t state)
+ audio_devices_t type, const std::string &address, audio_policy_dev_state_t state)
{
std::string criterionName = audio_is_output_device(type) ?
gOutputDeviceAddressCriterionName : gInputDeviceAddressCriterionName;
@@ -279,7 +294,7 @@
}
auto criterionType = criterion->getCriterionType();
- int deviceAddressId;
+ uint64_t deviceAddressId;
if (not criterionType->getNumericalValue(address.c_str(), deviceAddressId)) {
ALOGW("%s: unknown device address reported (%s) for criterion %s", __FUNCTION__,
address.c_str(), criterionName.c_str());
@@ -296,28 +311,28 @@
return NO_ERROR;
}
-status_t ParameterManagerWrapper::setAvailableInputDevices(audio_devices_t inputDevices)
+status_t ParameterManagerWrapper::setAvailableInputDevices(const DeviceTypeSet &types)
{
ISelectionCriterionInterface *criterion =
getElement<ISelectionCriterionInterface>(gInputDeviceCriterionName, mPolicyCriteria);
if (criterion == NULL) {
- ALOGE("%s: no criterion found for %s", __FUNCTION__, gInputDeviceCriterionName);
+ ALOGE("%s: no criterion found for %s", __func__, gInputDeviceCriterionName);
return DEAD_OBJECT;
}
- criterion->setCriterionState(inputDevices & ~AUDIO_DEVICE_BIT_IN);
+ criterion->setCriterionState(convertDeviceTypesToCriterionValue(types));
applyPlatformConfiguration();
return NO_ERROR;
}
-status_t ParameterManagerWrapper::setAvailableOutputDevices(audio_devices_t outputDevices)
+status_t ParameterManagerWrapper::setAvailableOutputDevices(const DeviceTypeSet &types)
{
ISelectionCriterionInterface *criterion =
getElement<ISelectionCriterionInterface>(gOutputDeviceCriterionName, mPolicyCriteria);
if (criterion == NULL) {
- ALOGE("%s: no criterion found for %s", __FUNCTION__, gOutputDeviceCriterionName);
+ ALOGE("%s: no criterion found for %s", __func__, gOutputDeviceCriterionName);
return DEAD_OBJECT;
}
- criterion->setCriterionState(outputDevices);
+ criterion->setCriterionState(convertDeviceTypesToCriterionValue(types));
applyPlatformConfiguration();
return NO_ERROR;
}
@@ -327,5 +342,45 @@
mPfwConnector->applyConfigurations();
}
+uint64_t ParameterManagerWrapper::convertDeviceTypeToCriterionValue(audio_devices_t type) const {
+ bool isOut = audio_is_output_devices(type);
+ uint32_t typeMask = isOut ? type : (type & ~AUDIO_DEVICE_BIT_IN);
+
+ const auto &adapters = isOut ? mOutputDeviceToCriterionTypeMap : mInputDeviceToCriterionTypeMap;
+ // Only multibit devices need adaptation.
+ if (popcount(typeMask) > 1) {
+ const auto &adapter = adapters.find(type);
+ if (adapter != adapters.end()) {
+ ALOGV("%s: multibit device %d converted to criterion %" PRIu64, __func__, type,
+ adapter->second);
+ return adapter->second;
+ }
+ ALOGE("%s: failed to find map for multibit device %d", __func__, type);
+ return 0;
+ }
+ return typeMask;
+}
+
+uint64_t ParameterManagerWrapper::convertDeviceTypesToCriterionValue(
+ const DeviceTypeSet &types) const {
+ uint64_t criterionValue = 0;
+ for (const auto &type : types) {
+ criterionValue += convertDeviceTypeToCriterionValue(type);
+ }
+ return criterionValue;
+}
+
+DeviceTypeSet ParameterManagerWrapper::convertDeviceCriterionValueToDeviceTypes(
+ uint64_t criterionValue, bool isOut) const {
+ DeviceTypeSet deviceTypes;
+ const auto &adapters = isOut ? mOutputDeviceToCriterionTypeMap : mInputDeviceToCriterionTypeMap;
+ for (const auto &adapter : adapters) {
+ if ((adapter.second & criterionValue) == adapter.second) {
+ deviceTypes.insert(adapter.first);
+ }
+ }
+ return deviceTypes;
+}
+
} // namespace audio_policy
} // namespace android
diff --git a/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h b/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h
index 62b129a..fa4ae1e 100644
--- a/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h
+++ b/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h
@@ -16,6 +16,7 @@
#pragma once
+#include <media/AudioContainers.h>
#include <system/audio.h>
#include <system/audio_policy.h>
#include <utils/Errors.h>
@@ -35,7 +36,8 @@
namespace android {
namespace audio_policy {
-using ValuePair = std::pair<uint32_t, std::string>;
+using ValuePair = std::tuple<uint64_t, uint32_t, std::string>;
+using DeviceToCriterionTypeAdapter = std::map<audio_devices_t, uint64_t>;
using ValuePairs = std::vector<ValuePair>;
class ParameterManagerWrapper
@@ -105,7 +107,7 @@
*
* @return NO_ERROR if devices criterion updated correctly, error code otherwise.
*/
- status_t setAvailableInputDevices(audio_devices_t inputDevices);
+ status_t setAvailableInputDevices(const DeviceTypeSet &inputDeviceTypes);
/**
* Set the available output devices i.e. set the associated policy parameter framework criterion
@@ -114,7 +116,7 @@
*
* @return NO_ERROR if devices criterion updated correctly, error code otherwise.
*/
- status_t setAvailableOutputDevices(audio_devices_t outputDevices);
+ status_t setAvailableOutputDevices(const DeviceTypeSet &outputDeviceTypes);
/**
* @brief setDeviceConnectionState propagates a state event on a given device(s)
@@ -124,7 +126,7 @@
* @return NO_ERROR if new state corretly propagated to Engine Parameter-Framework, error
* code otherwise.
*/
- status_t setDeviceConnectionState(audio_devices_t type, const std::string address,
+ status_t setDeviceConnectionState(audio_devices_t type, const std::string &address,
audio_policy_dev_state_t state);
/**
@@ -138,6 +140,13 @@
status_t addCriterion(const std::string &name, bool isInclusive, ValuePairs pairs,
const std::string &defaultValue);
+ uint64_t convertDeviceTypeToCriterionValue(audio_devices_t type) const;
+
+ uint64_t convertDeviceTypesToCriterionValue(const DeviceTypeSet &types) const;
+
+ DeviceTypeSet convertDeviceCriterionValueToDeviceTypes(
+ uint64_t criterionValue, bool isOut) const;
+
private:
/**
* Apply the configuration of the platform on the policy parameter manager.
@@ -211,6 +220,9 @@
template <typename T>
struct parameterManagerElementSupported;
+ DeviceToCriterionTypeAdapter mOutputDeviceToCriterionTypeMap;
+ DeviceToCriterionTypeAdapter mInputDeviceToCriterionTypeMap;
+
static const char *const mPolicyPfwDefaultConfFileName; /**< Default Policy PFW top file name.*/
static const char *const mPolicyPfwVendorConfFileName; /**< Vendor Policy PFW top file name.*/
};
diff --git a/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp b/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp
index 8584702..654e4bf 100644
--- a/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp
+++ b/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp
@@ -841,6 +841,8 @@
: AudioPolicyManagerFuzzerWithConfigurationFile(fdp){};
void process() override;
+ void fuzzGetDirectPlaybackSupport();
+
protected:
void setDeviceConnectionState();
void explicitlyRoutingAfterConnection();
@@ -891,10 +893,27 @@
}
}
+void AudioPolicyManagerFuzzerDeviceConnection::fuzzGetDirectPlaybackSupport() {
+ const uint32_t numTestCases = mFdp->ConsumeIntegralInRange<uint32_t>(1, 10);
+ for (int i = 0; i < numTestCases; ++i) {
+ audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
+ attr.content_type = getValueFromVector<audio_content_type_t>(mFdp, kAudioContentTypes);
+ attr.usage = getValueFromVector<audio_usage_t>(mFdp, kAudioUsages);
+ attr.source = getValueFromVector<audio_source_t>(mFdp, kAudioSources);
+ attr.flags = getValueFromVector<audio_flags_mask_t>(mFdp, kAudioFlagMasks);
+ audio_config_t config = AUDIO_CONFIG_INITIALIZER;
+ config.channel_mask = getValueFromVector<audio_channel_mask_t>(mFdp, kAudioChannelOutMasks);
+ config.format = getValueFromVector<audio_format_t>(mFdp, kAudioFormats);
+ config.sample_rate = getValueFromVector<uint32_t>(mFdp, kSamplingRates);
+ mManager->getDirectPlaybackSupport(&attr, &config);
+ }
+}
+
void AudioPolicyManagerFuzzerDeviceConnection::process() {
if (initialize()) {
setDeviceConnectionState();
explicitlyRoutingAfterConnection();
+ fuzzGetDirectPlaybackSupport();
fuzzPatchCreation();
}
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 05eae98..cc1012c 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -3662,53 +3662,7 @@
offloadInfo.stream_type, offloadInfo.bit_rate, offloadInfo.duration_us,
offloadInfo.has_video);
- if (mMasterMono) {
- return AUDIO_OFFLOAD_NOT_SUPPORTED; // no offloading if mono is set.
- }
-
- // Check if offload has been disabled
- if (property_get_bool("audio.offload.disable", false /* default_value */)) {
- ALOGV("%s: offload disabled by audio.offload.disable", __func__);
- return AUDIO_OFFLOAD_NOT_SUPPORTED;
- }
-
- // Check if stream type is music, then only allow offload as of now.
- if (offloadInfo.stream_type != AUDIO_STREAM_MUSIC)
- {
- ALOGV("%s: stream_type != MUSIC, returning false", __func__);
- return AUDIO_OFFLOAD_NOT_SUPPORTED;
- }
-
- //TODO: enable audio offloading with video when ready
- const bool allowOffloadWithVideo =
- property_get_bool("audio.offload.video", false /* default_value */);
- if (offloadInfo.has_video && !allowOffloadWithVideo) {
- ALOGV("%s: has_video == true, returning false", __func__);
- return AUDIO_OFFLOAD_NOT_SUPPORTED;
- }
-
- //If duration is less than minimum value defined in property, return false
- const int min_duration_secs = property_get_int32(
- "audio.offload.min.duration.secs", -1 /* default_value */);
- if (min_duration_secs >= 0) {
- if (offloadInfo.duration_us < min_duration_secs * 1000000LL) {
- ALOGV("%s: Offload denied by duration < audio.offload.min.duration.secs(=%d)",
- __func__, min_duration_secs);
- return AUDIO_OFFLOAD_NOT_SUPPORTED;
- }
- } else if (offloadInfo.duration_us < OFFLOAD_DEFAULT_MIN_DURATION_SECS * 1000000) {
- ALOGV("%s: Offload denied by duration < default min(=%u)",
- __func__, OFFLOAD_DEFAULT_MIN_DURATION_SECS);
- return AUDIO_OFFLOAD_NOT_SUPPORTED;
- }
-
- // Do not allow offloading if one non offloadable effect is enabled. This prevents from
- // creating an offloaded track and tearing it down immediately after start when audioflinger
- // detects there is an active non offloadable effect.
- // FIXME: We should check the audio session here but we do not have it in this context.
- // This may prevent offloading in rare situations where effects are left active by apps
- // in the background.
- if (mEffects.isNonOffloadableEffectEnabled()) {
+ if (!isOffloadPossible(offloadInfo)) {
return AUDIO_OFFLOAD_NOT_SUPPORTED;
}
@@ -3750,6 +3704,122 @@
return (profile != 0);
}
+bool AudioPolicyManager::isOffloadPossible(const audio_offload_info_t &offloadInfo,
+ bool durationIgnored) {
+ if (mMasterMono) {
+ return false; // no offloading if mono is set.
+ }
+
+ // Check if offload has been disabled
+ if (property_get_bool("audio.offload.disable", false /* default_value */)) {
+ ALOGV("%s: offload disabled by audio.offload.disable", __func__);
+ return false;
+ }
+
+ // Check if stream type is music, then only allow offload as of now.
+ if (offloadInfo.stream_type != AUDIO_STREAM_MUSIC)
+ {
+ ALOGV("%s: stream_type != MUSIC, returning false", __func__);
+ return false;
+ }
+
+ //TODO: enable audio offloading with video when ready
+ const bool allowOffloadWithVideo =
+ property_get_bool("audio.offload.video", false /* default_value */);
+ if (offloadInfo.has_video && !allowOffloadWithVideo) {
+ ALOGV("%s: has_video == true, returning false", __func__);
+ return false;
+ }
+
+ //If duration is less than minimum value defined in property, return false
+ const int min_duration_secs = property_get_int32(
+ "audio.offload.min.duration.secs", -1 /* default_value */);
+ if (!durationIgnored) {
+ if (min_duration_secs >= 0) {
+ if (offloadInfo.duration_us < min_duration_secs * 1000000LL) {
+ ALOGV("%s: Offload denied by duration < audio.offload.min.duration.secs(=%d)",
+ __func__, min_duration_secs);
+ return false;
+ }
+ } else if (offloadInfo.duration_us < OFFLOAD_DEFAULT_MIN_DURATION_SECS * 1000000) {
+ ALOGV("%s: Offload denied by duration < default min(=%u)",
+ __func__, OFFLOAD_DEFAULT_MIN_DURATION_SECS);
+ return false;
+ }
+ }
+
+ // Do not allow offloading if one non offloadable effect is enabled. This prevents from
+ // creating an offloaded track and tearing it down immediately after start when audioflinger
+ // detects there is an active non offloadable effect.
+ // FIXME: We should check the audio session here but we do not have it in this context.
+ // This may prevent offloading in rare situations where effects are left active by apps
+ // in the background.
+ if (mEffects.isNonOffloadableEffectEnabled()) {
+ return false;
+ }
+
+ return true;
+}
+
+audio_direct_mode_t AudioPolicyManager::getDirectPlaybackSupport(const audio_attributes_t *attr,
+ const audio_config_t *config) {
+ audio_offload_info_t offloadInfo = AUDIO_INFO_INITIALIZER;
+ offloadInfo.format = config->format;
+ offloadInfo.sample_rate = config->sample_rate;
+ offloadInfo.channel_mask = config->channel_mask;
+ offloadInfo.stream_type = mEngine->getStreamTypeForAttributes(*attr);
+ offloadInfo.has_video = false;
+ offloadInfo.is_streaming = false;
+ const bool offloadPossible = isOffloadPossible(offloadInfo, true /*durationIgnored*/);
+
+ audio_direct_mode_t directMode = AUDIO_DIRECT_NOT_SUPPORTED;
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE;
+ audio_flags_to_audio_output_flags(attr->flags, &flags);
+ // only retain flags that will drive compressed offload or passthrough
+ uint32_t relevantFlags = AUDIO_OUTPUT_FLAG_HW_AV_SYNC;
+ if (offloadPossible) {
+ relevantFlags |= AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
+ }
+ flags = (audio_output_flags_t)((flags & relevantFlags) | AUDIO_OUTPUT_FLAG_DIRECT);
+
+ for (const auto& hwModule : mHwModules) {
+ for (const auto& curProfile : hwModule->getOutputProfiles()) {
+ if (!curProfile->isCompatibleProfile(DeviceVector(),
+ config->sample_rate, nullptr /*updatedSamplingRate*/,
+ config->format, nullptr /*updatedFormat*/,
+ config->channel_mask, nullptr /*updatedChannelMask*/,
+ flags)) {
+ continue;
+ }
+ // reject profiles not corresponding to a device currently available
+ if (!mAvailableOutputDevices.containsAtLeastOne(curProfile->getSupportedDevices())) {
+ continue;
+ }
+ if ((curProfile->getFlags() & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
+ != AUDIO_OUTPUT_FLAG_NONE) {
+ if ((directMode & AUDIO_DIRECT_OFFLOAD_GAPLESS_SUPPORTED)
+ != AUDIO_DIRECT_NOT_SUPPORTED) {
+ // Already reports offload gapless supported. No need to report offload support.
+ continue;
+ }
+ if ((curProfile->getFlags() & AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD)
+ != AUDIO_OUTPUT_FLAG_NONE) {
+ // If offload gapless is reported, no need to report offload support.
+ directMode = (audio_direct_mode_t) ((directMode &
+ ~AUDIO_DIRECT_OFFLOAD_SUPPORTED) |
+ AUDIO_DIRECT_OFFLOAD_GAPLESS_SUPPORTED);
+ } else {
+ directMode = (audio_direct_mode_t)(directMode |AUDIO_DIRECT_OFFLOAD_SUPPORTED);
+ }
+ } else {
+ directMode = (audio_direct_mode_t) (directMode |
+ AUDIO_DIRECT_BITSTREAM_SUPPORTED);
+ }
+ }
+ }
+ return directMode;
+}
+
status_t AudioPolicyManager::listAudioPorts(audio_port_role_t role,
audio_port_type_t type,
unsigned int *num_ports,
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 8a85b95..bdeba3d 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -366,6 +366,9 @@
virtual status_t releaseSpatializerOutput(audio_io_handle_t output);
+ virtual audio_direct_mode_t getDirectPlaybackSupport(const audio_attributes_t *attr,
+ const audio_config_t *config);
+
bool isCallScreenModeSupported() override;
void onNewAudioModulesAvailable() override;
@@ -1059,6 +1062,9 @@
sp<SwAudioOutputDescriptor> openOutputWithProfileAndDevice(const sp<IOProfile>& profile,
const DeviceVector& devices);
+ bool isOffloadPossible(const audio_offload_info_t& offloadInfo,
+ bool durationIgnored = false);
+
};
};
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 1fbea7d..8ddd2a4 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -2334,4 +2334,24 @@
return Status::ok();
}
+Status AudioPolicyService::getDirectPlaybackSupport(const media::AudioAttributesInternal &attrAidl,
+ const AudioConfig &configAidl,
+ media::AudioDirectMode *_aidl_return) {
+ if (mAudioPolicyManager == nullptr) {
+ return binderStatusFromStatusT(NO_INIT);
+ }
+ if (_aidl_return == nullptr) {
+ return binderStatusFromStatusT(BAD_VALUE);
+ }
+ audio_attributes_t attr = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioAttributesInternal_audio_attributes_t(attrAidl));
+ audio_config_t config = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioConfig_audio_config_t(configAidl, false /*isInput*/));
+ Mutex::Autolock _l(mLock);
+ *_aidl_return = static_cast<media::AudioDirectMode>(
+ VALUE_OR_RETURN_BINDER_STATUS(legacy2aidl_audio_direct_mode_t_int32_t_mask(
+ mAudioPolicyManager->getDirectPlaybackSupport(&attr, &config))));
+ return Status::ok();
+}
+
} // namespace android
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index b3ac21b..84b1e50 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -272,6 +272,10 @@
const std::vector<AudioDevice>& devices,
bool* _aidl_return) override;
+ binder::Status getDirectPlaybackSupport(const media::AudioAttributesInternal& attr,
+ const AudioConfig& config,
+ media::AudioDirectMode* _aidl_return) override;
+
status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
// IBinder::DeathRecipient
diff --git a/services/audiopolicy/service/SpatializerPoseController.cpp b/services/audiopolicy/service/SpatializerPoseController.cpp
index ffedf63..80a3d29 100644
--- a/services/audiopolicy/service/SpatializerPoseController.cpp
+++ b/services/audiopolicy/service/SpatializerPoseController.cpp
@@ -45,14 +45,14 @@
// determine the time constants used for high-pass filtering those readings. If the value is set
// too high, we may experience drift. If it is set too low, we may experience poses tending toward
// identity too fast.
-constexpr auto kTranslationalDriftTimeConstant = 20s;
+constexpr auto kTranslationalDriftTimeConstant = 40s;
// This should be set to the typical time scale that the rotation sensors used drift in. This
// means, loosely, for how long we can trust the reading to be "accurate enough". This would
// determine the time constants used for high-pass filtering those readings. If the value is set
// too high, we may experience drift. If it is set too low, we may experience poses tending toward
// identity too fast.
-constexpr auto kRotationalDriftTimeConstant = 20s;
+constexpr auto kRotationalDriftTimeConstant = 60s;
// This is how far into the future we predict the head pose, using linear extrapolation based on
// twist (velocity). It should be set to a value that matches the characteristic durations of moving
@@ -64,6 +64,25 @@
// stale;
constexpr auto kMaxLostSamples = 4;
+// Auto-recenter kicks in after the head has been still for this long.
+constexpr auto kAutoRecenterWindowDuration = 10s;
+
+// Auto-recenter considers head not still if translated by this much (in meters, approx).
+constexpr float kAutoRecenterTranslationThreshold = 0.1f;
+
+// Auto-recenter considers head not still if rotated by this much (in radians, approx).
+constexpr float kAutoRecenterRotationThreshold = 5.0f / 180 * M_PI;
+
+// Screen is considered to be unstable (not still) if it has moved significantly within the last
+// time window of this duration.
+constexpr auto kScreenStillnessWindowDuration = 10s;
+
+// Screen is considered to have moved significantly if translated by this much (in meter, approx).
+constexpr float kScreenStillnessTranslationThreshold = 0.1f;
+
+// Screen is considered to have moved significantly if rotated by this much (in radians, approx).
+constexpr float kScreenStillnessRotationThreshold = 5.0f / 180 * M_PI;
+
// Time units for system clock ticks. This is what the Sensor Framework timestamps represent and
// what we use for pose filtering.
using Ticks = std::chrono::nanoseconds;
@@ -81,10 +100,17 @@
mProcessor(createHeadTrackingProcessor(HeadTrackingProcessor::Options{
.maxTranslationalVelocity = kMaxTranslationalVelocity / kTicksPerSecond,
.maxRotationalVelocity = kMaxRotationalVelocity / kTicksPerSecond,
- .translationalDriftTimeConstant = Ticks(kTranslationalDriftTimeConstant).count(),
- .rotationalDriftTimeConstant = Ticks(kRotationalDriftTimeConstant).count(),
+ .translationalDriftTimeConstant =
+ double(Ticks(kTranslationalDriftTimeConstant).count()),
+ .rotationalDriftTimeConstant = double(Ticks(kRotationalDriftTimeConstant).count()),
.freshnessTimeout = Ticks(sensorPeriod * kMaxLostSamples).count(),
.predictionDuration = Ticks(kPredictionDuration).count(),
+ .autoRecenterWindowDuration = Ticks(kAutoRecenterWindowDuration).count(),
+ .autoRecenterTranslationalThreshold = kAutoRecenterTranslationThreshold,
+ .autoRecenterRotationalThreshold = kAutoRecenterRotationThreshold,
+ .screenStillnessWindowDuration = Ticks(kScreenStillnessWindowDuration).count(),
+ .screenStillnessTranslationalThreshold = kScreenStillnessTranslationThreshold,
+ .screenStillnessRotationalThreshold = kScreenStillnessRotationThreshold,
})),
mPoseProvider(SensorPoseProvider::create("headtracker", this)),
mThread([this, maxUpdatePeriod] {
diff --git a/services/audiopolicy/tests/AudioPolicyTestManager.h b/services/audiopolicy/tests/AudioPolicyTestManager.h
index 7f67940..9d0d558 100644
--- a/services/audiopolicy/tests/AudioPolicyTestManager.h
+++ b/services/audiopolicy/tests/AudioPolicyTestManager.h
@@ -33,6 +33,7 @@
using AudioPolicyManager::releaseMsdOutputPatches;
using AudioPolicyManager::setMsdOutputPatches;
using AudioPolicyManager::getAudioPatches;
+ using AudioPolicyManager::getDirectPlaybackSupport;
uint32_t getAudioPortGeneration() const { return mAudioPortGeneration; }
};
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index b9c1260..046b84b 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -242,6 +242,14 @@
setFormat(config.format);
setSampleRate(config.sample_rate);
+ // If the position is not updated while the timestamp is updated for more than a certain amount,
+ // the timestamp reported from the HAL may not be accurate. Here, a timestamp grace period is
+ // set as 5 burst size. We may want to update this value if there is any report from OEMs saying
+ // that is too short.
+ static constexpr int kTimestampGraceBurstCount = 5;
+ mTimestampGracePeriodMs = ((int64_t) kTimestampGraceBurstCount * mFramesPerBurst
+ * AAUDIO_MILLIS_PER_SECOND) / getSampleRate();
+
ALOGD("%s() actual rate = %d, channels = %d channelMask = %#x, deviceId = %d, capacity = %d\n",
__func__, getSampleRate(), getSamplesPerFrame(), getChannelMask(),
deviceId, getBufferCapacity());
@@ -418,14 +426,79 @@
aaudio_result_t AAudioServiceEndpointMMAP::getExternalPosition(uint64_t *positionFrames,
int64_t *timeNanos)
{
- if (!mExternalPositionSupported) {
- return AAUDIO_ERROR_INVALID_STATE;
+ if (mHalExternalPositionStatus != AAUDIO_OK) {
+ return mHalExternalPositionStatus;
}
- status_t status = mMmapStream->getExternalPosition(positionFrames, timeNanos);
- if (status == INVALID_OPERATION) {
- // getExternalPosition is not supported. Set mExternalPositionSupported as false
+ uint64_t tempPositionFrames;
+ int64_t tempTimeNanos;
+ status_t status = mMmapStream->getExternalPosition(&tempPositionFrames, &tempTimeNanos);
+ if (status != OK) {
+ // getExternalPosition reports error. The HAL may not support the API. Cache the result
// so that the call will not go to the HAL next time.
- mExternalPositionSupported = false;
+ mHalExternalPositionStatus = AAudioConvert_androidToAAudioResult(status);
+ return mHalExternalPositionStatus;
}
- return AAudioConvert_androidToAAudioResult(status);
+
+ // If the HAL keeps reporting the same position or timestamp, the HAL may be having some issues
+ // to report correct external position. In that case, we will not trust the values reported from
+ // the HAL. Ideally, we may want to stop querying external position if the HAL cannot report
+ // correct position within a period. But it may not be a good idea to get system time too often.
+ // In that case, a maximum number of frozen external position is defined so that if the
+ // count of the same timestamp or position is reported by the HAL continuously, the values from
+ // the HAL will no longer be trusted.
+ static constexpr int kMaxFrozenCount = 20;
+ // If the HAL version is less than 7.0, the getPresentationPosition is an optional API.
+ // If the HAL version is 7.0 or later, the getPresentationPosition is a mandatory API.
+ // In that case, even the returned status is NO_ERROR, it doesn't indicate the returned
+ // position is a valid one. Do a simple validation, which is checking if the position is
+ // forward within half a second or not, here so that this function can return error if
+ // the validation fails. Note that we don't only apply this validation logic to HAL API
+ // less than 7.0. The reason is that there is a chance the HAL is not reporting the
+ // timestamp and position correctly.
+ if (mLastPositionFrames > tempPositionFrames) {
+ // If the position is going backwards, there must be something wrong with the HAL.
+ // In that case, we do not trust the values reported by the HAL.
+ ALOGW("%s position is going backwards, last position(%jd) current position(%jd)",
+ __func__, mLastPositionFrames, tempPositionFrames);
+ mHalExternalPositionStatus = AAUDIO_ERROR_INTERNAL;
+ return mHalExternalPositionStatus;
+ } else if (mLastPositionFrames == tempPositionFrames) {
+ if (tempTimeNanos - mTimestampNanosForLastPosition >
+ AAUDIO_NANOS_PER_MILLISECOND * mTimestampGracePeriodMs) {
+ ALOGW("%s, the reported position is not changed within %d msec. "
+ "Set the external position as not supported", __func__, mTimestampGracePeriodMs);
+ mHalExternalPositionStatus = AAUDIO_ERROR_INTERNAL;
+ return mHalExternalPositionStatus;
+ }
+ mFrozenPositionCount++;
+ } else {
+ mFrozenPositionCount = 0;
+ }
+
+ if (mTimestampNanosForLastPosition > tempTimeNanos) {
+ // If the timestamp is going backwards, there must be something wrong with the HAL.
+ // In that case, we do not trust the values reported by the HAL.
+ ALOGW("%s timestamp is going backwards, last timestamp(%jd), current timestamp(%jd)",
+ __func__, mTimestampNanosForLastPosition, tempTimeNanos);
+ mHalExternalPositionStatus = AAUDIO_ERROR_INTERNAL;
+ return mHalExternalPositionStatus;
+ } else if (mTimestampNanosForLastPosition == tempTimeNanos) {
+ mFrozenTimestampCount++;
+ } else {
+ mFrozenTimestampCount = 0;
+ }
+
+ if (mFrozenTimestampCount + mFrozenPositionCount > kMaxFrozenCount) {
+ ALOGW("%s too many frozen external position from HAL.", __func__);
+ mHalExternalPositionStatus = AAUDIO_ERROR_INTERNAL;
+ return mHalExternalPositionStatus;
+ }
+
+ mLastPositionFrames = tempPositionFrames;
+ mTimestampNanosForLastPosition = tempTimeNanos;
+
+ // Only update the timestamp and position when they looks valid.
+ *positionFrames = tempPositionFrames;
+ *timeNanos = tempTimeNanos;
+ return mHalExternalPositionStatus;
}
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.h b/services/oboeservice/AAudioServiceEndpointMMAP.h
index ddfac63..6314e5e 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.h
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.h
@@ -106,7 +106,12 @@
int64_t mHardwareTimeOffsetNanos = 0; // TODO get from HAL
- bool mExternalPositionSupported = true;
+ aaudio_result_t mHalExternalPositionStatus = AAUDIO_OK;
+ uint64_t mLastPositionFrames = 0;
+ int64_t mTimestampNanosForLastPosition = 0;
+ int32_t mTimestampGracePeriodMs;
+ int32_t mFrozenPositionCount = 0;
+ int32_t mFrozenTimestampCount = 0;
};
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 05b7f7d..ffc16ac 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -167,7 +167,6 @@
// If it fails, get timestamp that was written by getFreeRunningPosition()
aaudio_result_t AAudioServiceStreamMMAP::getHardwareTimestamp_l(int64_t *positionFrames,
int64_t *timeNanos) {
-
sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
if (endpoint == nullptr) {
ALOGE("%s() has no endpoint", __func__);
@@ -176,17 +175,17 @@
sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP =
static_cast<AAudioServiceEndpointMMAP *>(endpoint.get());
- // Disable this code temporarily because the HAL is not returning
- // a useful result.
-#if 0
uint64_t position;
- if (serviceEndpointMMAP->getExternalPosition(&position, timeNanos) == AAUDIO_OK) {
- ALOGD("%s() getExternalPosition() says pos = %" PRIi64 ", time = %" PRIi64,
+ aaudio_result_t result = serviceEndpointMMAP->getExternalPosition(&position, timeNanos);
+ if (result == AAUDIO_OK) {
+ ALOGV("%s() getExternalPosition() says pos = %" PRIi64 ", time = %" PRIi64,
__func__, position, *timeNanos);
*positionFrames = (int64_t) position;
return AAUDIO_OK;
- } else
-#endif
+ } else {
+ ALOGV("%s() getExternalPosition() returns error %d", __func__, result);
+ }
+
if (mAtomicStreamTimestamp.isValid()) {
Timestamp timestamp = mAtomicStreamTimestamp.read();
*positionFrames = timestamp.getPosition();
diff --git a/services/tuner/TunerFrontend.cpp b/services/tuner/TunerFrontend.cpp
index e86e8e1..a5ef2bb 100644
--- a/services/tuner/TunerFrontend.cpp
+++ b/services/tuner/TunerFrontend.cpp
@@ -115,16 +115,6 @@
return mFrontend->setLnb(static_cast<TunerLnb*>(lnb.get())->getId());
}
-::ndk::ScopedAStatus TunerFrontend::setLna(bool bEnable) {
- if (mFrontend == nullptr) {
- ALOGD("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
- return mFrontend->setLna(bEnable);
-}
-
::ndk::ScopedAStatus TunerFrontend::linkCiCamToFrontend(int32_t ciCamId, int32_t* _aidl_return) {
if (mFrontend == nullptr) {
ALOGD("IFrontend is not initialized");
@@ -174,6 +164,16 @@
return ::ndk::ScopedAStatus::ok();
}
+::ndk::ScopedAStatus TunerFrontend::getHardwareInfo(std::string* _aidl_return) {
+ if (mFrontend == nullptr) {
+ ALOGD("IFrontend is not initialized");
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ return mFrontend->getHardwareInfo(_aidl_return);
+}
+
/////////////// FrontendCallback ///////////////////////
::ndk::ScopedAStatus TunerFrontend::FrontendCallback::onEvent(FrontendEventType frontendEventType) {
ALOGV("FrontendCallback::onEvent, type=%d", frontendEventType);
diff --git a/services/tuner/TunerFrontend.h b/services/tuner/TunerFrontend.h
index 417d969..418a751 100644
--- a/services/tuner/TunerFrontend.h
+++ b/services/tuner/TunerFrontend.h
@@ -56,13 +56,13 @@
FrontendScanType in_frontendScanType) override;
::ndk::ScopedAStatus stopScan() override;
::ndk::ScopedAStatus setLnb(const shared_ptr<ITunerLnb>& in_lnb) override;
- ::ndk::ScopedAStatus setLna(bool in_bEnable) override;
::ndk::ScopedAStatus linkCiCamToFrontend(int32_t in_ciCamId, int32_t* _aidl_return) override;
::ndk::ScopedAStatus unlinkCiCamToFrontend(int32_t in_ciCamId) override;
::ndk::ScopedAStatus close() override;
::ndk::ScopedAStatus getStatus(const vector<FrontendStatusType>& in_statusTypes,
vector<FrontendStatus>* _aidl_return) override;
::ndk::ScopedAStatus getFrontendId(int32_t* _aidl_return) override;
+ ::ndk::ScopedAStatus getHardwareInfo(std::string* _aidl_return) override;
struct FrontendCallback : public BnFrontendCallback {
FrontendCallback(const shared_ptr<ITunerFrontendCallback> tunerFrontendCallback)
diff --git a/services/tuner/TunerService.cpp b/services/tuner/TunerService.cpp
index 36e4cd1..335578d 100644
--- a/services/tuner/TunerService.cpp
+++ b/services/tuner/TunerService.cpp
@@ -260,6 +260,16 @@
return ::ndk::ScopedAStatus::ok();
}
+::ndk::ScopedAStatus TunerService::setLna(bool bEnable) {
+ if (!hasITuner()) {
+ ALOGD("get ITuner failed");
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ return mTuner->setLna(bEnable);
+}
+
string TunerService::addFilterToShared(const shared_ptr<TunerFilter>& sharedFilter) {
Mutex::Autolock _l(mSharedFiltersLock);
diff --git a/services/tuner/TunerService.h b/services/tuner/TunerService.h
index 7bf50b6..f144744 100644
--- a/services/tuner/TunerService.h
+++ b/services/tuner/TunerService.h
@@ -76,6 +76,7 @@
::ndk::ScopedAStatus openSharedFilter(const string& in_filterToken,
const shared_ptr<ITunerFilterCallback>& in_cb,
shared_ptr<ITunerFilter>* _aidl_return) override;
+ ::ndk::ScopedAStatus setLna(bool in_bEnable) override;
string addFilterToShared(const shared_ptr<TunerFilter>& sharedFilter);
void removeSharedFilter(const shared_ptr<TunerFilter>& sharedFilter);
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerFrontend.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerFrontend.aidl
index 771a647..96f285f 100644
--- a/services/tuner/aidl/android/media/tv/tuner/ITunerFrontend.aidl
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerFrontend.aidl
@@ -69,13 +69,6 @@
void setLnb(in ITunerLnb lnb);
/**
- * Enable or Disable Low Noise Amplifier (LNA).
- *
- * @param bEnable enable Lna or not.
- */
- void setLna(in boolean bEnable);
-
- /**
* Link Frontend to the cicam with given id.
*
* @return lts id
@@ -101,4 +94,9 @@
* Gets the id of the frontend.
*/
int getFrontendId();
+
+ /**
+ * Request hardware information about the frontend.
+ */
+ String getHardwareInfo();
}
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerService.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerService.aidl
index e6a1a5c..248077b 100644
--- a/services/tuner/aidl/android/media/tv/tuner/ITunerService.aidl
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerService.aidl
@@ -104,4 +104,11 @@
* @return a newly created ITunerFilter interface.
*/
ITunerFilter openSharedFilter(in String filterToken, in ITunerFilterCallback cb);
+
+ /**
+ * Enable or Disable Low Noise Amplifier (LNA).
+ *
+ * @param bEnable enable Lna or not.
+ */
+ void setLna(in boolean bEnable);
}
diff --git a/services/tuner/hidl/TunerHidlFilter.cpp b/services/tuner/hidl/TunerHidlFilter.cpp
index b738b57..a5bbf39 100644
--- a/services/tuner/hidl/TunerHidlFilter.cpp
+++ b/services/tuner/hidl/TunerHidlFilter.cpp
@@ -29,6 +29,7 @@
#include "TunerHidlService.h"
using ::aidl::android::hardware::tv::tuner::AudioExtraMetaData;
+using ::aidl::android::hardware::tv::tuner::AudioStreamType;
using ::aidl::android::hardware::tv::tuner::Constant;
using ::aidl::android::hardware::tv::tuner::DemuxAlpFilterSettings;
using ::aidl::android::hardware::tv::tuner::DemuxAlpFilterSettingsFilterSettings;
@@ -604,7 +605,11 @@
bool TunerHidlFilter::getHidlAvStreamType(const AvStreamType avStreamType, HidlAvStreamType& type) {
if (isAudioFilter()) {
- type.audio(static_cast<HidlAudioStreamType>(avStreamType.get<AvStreamType::audio>()));
+ AudioStreamType audio = avStreamType.get<AvStreamType::audio>();
+ if (static_cast<int32_t>(audio) > static_cast<int32_t>(HidlAudioStreamType::DRA)) {
+ return false;
+ }
+ type.audio(static_cast<HidlAudioStreamType>(audio));
return true;
}
@@ -1044,6 +1049,8 @@
media.avDataId = static_cast<int64_t>(mediaEvent.avDataId);
media.mpuSequenceNumber = static_cast<int32_t>(mediaEvent.mpuSequenceNumber);
media.isPesPrivateData = mediaEvent.isPesPrivateData;
+ media.scIndexMask.set<DemuxFilterScIndexMask::scIndex>(
+ static_cast<int32_t>(DemuxScIndex::UNDEFINED));
if (mediaEvent.extraMetaData.getDiscriminator() ==
HidlDemuxFilterMediaEvent::ExtraMetaData::hidl_discriminator::audio) {
diff --git a/services/tuner/hidl/TunerHidlFrontend.cpp b/services/tuner/hidl/TunerHidlFrontend.cpp
index 1f28406..057f24a 100644
--- a/services/tuner/hidl/TunerHidlFrontend.cpp
+++ b/services/tuner/hidl/TunerHidlFrontend.cpp
@@ -22,6 +22,7 @@
#include <aidl/android/hardware/tv/tuner/Result.h>
#include "TunerHidlLnb.h"
+#include "TunerHidlService.h"
using ::aidl::android::hardware::tv::tuner::FrontendAnalogSettings;
using ::aidl::android::hardware::tv::tuner::FrontendAnalogSifStandard;
@@ -309,21 +310,6 @@
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(status));
}
-::ndk::ScopedAStatus TunerHidlFrontend::setLna(bool bEnable) {
- if (mFrontend == nullptr) {
- ALOGD("IFrontend is not initialized");
- return ::ndk::ScopedAStatus::fromServiceSpecificError(
- static_cast<int32_t>(Result::UNAVAILABLE));
- }
-
- HidlResult status = mFrontend->setLna(bEnable);
- if (status == HidlResult::SUCCESS) {
- return ::ndk::ScopedAStatus::ok();
- }
-
- return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(status));
-}
-
::ndk::ScopedAStatus TunerHidlFrontend::linkCiCamToFrontend(int32_t ciCamId,
int32_t* _aidl_return) {
if (mFrontend_1_1 == nullptr) {
@@ -369,6 +355,8 @@
static_cast<int32_t>(Result::UNAVAILABLE));
}
+ TunerHidlService::getTunerService()->removeFrontend(this->ref<TunerHidlFrontend>());
+
HidlResult status = mFrontend->close();
mFrontend = nullptr;
mFrontend_1_1 = nullptr;
@@ -434,6 +422,21 @@
return ::ndk::ScopedAStatus::ok();
}
+::ndk::ScopedAStatus TunerHidlFrontend::getHardwareInfo(std::string* _aidl_return) {
+ _aidl_return->clear();
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(Result::UNAVAILABLE));
+}
+
+void TunerHidlFrontend::setLna(bool bEnable) {
+ if (mFrontend == nullptr) {
+ ALOGD("IFrontend is not initialized");
+ return;
+ }
+
+ mFrontend->setLna(bEnable);
+}
+
/////////////// FrontendCallback ///////////////////////
Return<void> TunerHidlFrontend::FrontendCallback::onEvent(HidlFrontendEventType frontendEventType) {
ALOGV("FrontendCallback::onEvent, type=%d", frontendEventType);
diff --git a/services/tuner/hidl/TunerHidlFrontend.h b/services/tuner/hidl/TunerHidlFrontend.h
index 6a3a04a..7ff278c 100644
--- a/services/tuner/hidl/TunerHidlFrontend.h
+++ b/services/tuner/hidl/TunerHidlFrontend.h
@@ -76,13 +76,15 @@
FrontendScanType in_frontendScanType) override;
::ndk::ScopedAStatus stopScan() override;
::ndk::ScopedAStatus setLnb(const shared_ptr<ITunerLnb>& in_lnb) override;
- ::ndk::ScopedAStatus setLna(bool in_bEnable) override;
::ndk::ScopedAStatus linkCiCamToFrontend(int32_t in_ciCamId, int32_t* _aidl_return) override;
::ndk::ScopedAStatus unlinkCiCamToFrontend(int32_t in_ciCamId) override;
::ndk::ScopedAStatus close() override;
::ndk::ScopedAStatus getStatus(const vector<FrontendStatusType>& in_statusTypes,
vector<FrontendStatus>* _aidl_return) override;
::ndk::ScopedAStatus getFrontendId(int32_t* _aidl_return) override;
+ ::ndk::ScopedAStatus getHardwareInfo(std::string* _aidl_return) override;
+
+ void setLna(bool in_bEnable);
struct FrontendCallback : public HidlIFrontendCallback {
FrontendCallback(const shared_ptr<ITunerFrontendCallback> tunerFrontendCallback)
diff --git a/services/tuner/hidl/TunerHidlService.cpp b/services/tuner/hidl/TunerHidlService.cpp
index f4b0cde..f4aa61a 100644
--- a/services/tuner/hidl/TunerHidlService.cpp
+++ b/services/tuner/hidl/TunerHidlService.cpp
@@ -74,7 +74,10 @@
updateTunerResources();
}
-TunerHidlService::~TunerHidlService() {}
+TunerHidlService::~TunerHidlService() {
+ mOpenedFrontends.clear();
+ mLnaStatus = -1;
+}
binder_status_t TunerHidlService::instantiate() {
if (HidlITuner::getService() == nullptr) {
@@ -237,7 +240,17 @@
if (status != HidlResult::SUCCESS) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(status));
}
- *_aidl_return = ::ndk::SharedRefBase::make<TunerHidlFrontend>(frontend, id);
+
+ shared_ptr<TunerHidlFrontend> tunerFrontend =
+ ::ndk::SharedRefBase::make<TunerHidlFrontend>(frontend, id);
+ if (mLnaStatus != -1) {
+ tunerFrontend->setLna(mLnaStatus == 1);
+ }
+ {
+ Mutex::Autolock _l(mOpenedFrontendsLock);
+ mOpenedFrontends.insert(tunerFrontend);
+ }
+ *_aidl_return = tunerFrontend;
return ::ndk::ScopedAStatus::ok();
}
@@ -355,6 +368,25 @@
return ::ndk::ScopedAStatus::ok();
}
+::ndk::ScopedAStatus TunerHidlService::setLna(bool bEnable) {
+ if (!hasITuner()) {
+ ALOGE("get ITuner failed");
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(Result::UNAVAILABLE));
+ }
+
+ mLnaStatus = bEnable ? 1 : 0;
+
+ {
+ Mutex::Autolock _l(mOpenedFrontendsLock);
+ for (auto it = mOpenedFrontends.begin(); it != mOpenedFrontends.end(); ++it) {
+ (*it)->setLna(mLnaStatus == 1);
+ }
+ }
+
+ return ::ndk::ScopedAStatus::ok();
+}
+
string TunerHidlService::addFilterToShared(const shared_ptr<TunerHidlFilter>& sharedFilter) {
Mutex::Autolock _l(mSharedFiltersLock);
@@ -372,6 +404,16 @@
mSharedFilters.erase(to_string(reinterpret_cast<std::uintptr_t>(sharedFilter.get())));
}
+void TunerHidlService::removeFrontend(const shared_ptr<TunerHidlFrontend>& frontend) {
+ Mutex::Autolock _l(mOpenedFrontendsLock);
+ for (auto it = mOpenedFrontends.begin(); it != mOpenedFrontends.end(); ++it) {
+ if (it->get() == frontend.get()) {
+ mOpenedFrontends.erase(it);
+ break;
+ }
+ }
+}
+
void TunerHidlService::updateTunerResources() {
if (!hasITuner()) {
ALOGE("Failed to updateTunerResources");
diff --git a/services/tuner/hidl/TunerHidlService.h b/services/tuner/hidl/TunerHidlService.h
index 2b8750e..4cff7cf 100644
--- a/services/tuner/hidl/TunerHidlService.h
+++ b/services/tuner/hidl/TunerHidlService.h
@@ -24,8 +24,11 @@
#include <android/hardware/tv/tuner/1.1/ITuner.h>
#include <utils/Mutex.h>
+#include <unordered_set>
+
#include "TunerHelper.h"
#include "TunerHidlFilter.h"
+#include "TunerHidlFrontend.h"
using ::aidl::android::hardware::tv::tuner::DemuxCapabilities;
using ::aidl::android::hardware::tv::tuner::DemuxFilterEvent;
@@ -85,9 +88,11 @@
::ndk::ScopedAStatus openSharedFilter(const string& in_filterToken,
const shared_ptr<ITunerFilterCallback>& in_cb,
shared_ptr<ITunerFilter>* _aidl_return) override;
+ ::ndk::ScopedAStatus setLna(bool in_bEnable) override;
string addFilterToShared(const shared_ptr<TunerHidlFilter>& sharedFilter);
void removeSharedFilter(const shared_ptr<TunerHidlFilter>& sharedFilter);
+ void removeFrontend(const shared_ptr<TunerHidlFrontend>& frontend);
static shared_ptr<TunerHidlService> getTunerService();
@@ -108,6 +113,9 @@
int mTunerVersion = TUNER_HAL_VERSION_UNKNOWN;
Mutex mSharedFiltersLock;
map<string, shared_ptr<TunerHidlFilter>> mSharedFilters;
+ Mutex mOpenedFrontendsLock;
+ unordered_set<shared_ptr<TunerHidlFrontend>> mOpenedFrontends;
+ int mLnaStatus = -1;
static shared_ptr<TunerHidlService> sTunerService;
};