APM: support product strategy routing

Audio policy engine supports receiving a preferred device to use
for a given strategy (Engine superclass). Use of the preferred
device intervenes at the level of the each engine implementation,
here in the default engine in getDevicesForProductStrategy() method
so it is saved in the routing cache, and respects existing routing
priorities.
  Refactor the loops for call and output rerouting into a new
updateCallAndOutputRouting() method.

Bug: 144440677
Test: atest AudioServiceHostTest#testPreferredDeviceRouting

Change-Id: Ic4c690e1b0d8020c4335979e40e14e6df5887879
Merged-In: Ic4c690e1b0d8020c4335979e40e14e6df5887879
gugelfrei
Jean-Michel Trivi 5 years ago
parent f6cd76eef9
commit 2deb47896c

@ -1525,6 +1525,35 @@ status_t AudioSystem::setRttEnabled(bool enabled)
return aps->setRttEnabled(enabled);
}
status_t AudioSystem::setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) {
return PERMISSION_DENIED;
}
return aps->setPreferredDeviceForStrategy(strategy, device);
}
status_t AudioSystem::removePreferredDeviceForStrategy(product_strategy_t strategy)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) {
return PERMISSION_DENIED;
}
return aps->removePreferredDeviceForStrategy(strategy);
}
status_t AudioSystem::getPreferredDeviceForStrategy(product_strategy_t strategy,
AudioDeviceTypeAddr &device)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) {
return PERMISSION_DENIED;
}
return aps->getPreferredDeviceForStrategy(strategy, device);
}
// ---------------------------------------------------------------------------
int AudioSystem::AudioPolicyServiceClient::addAudioPortCallback(

@ -104,7 +104,10 @@ enum {
GET_VOLUME_GROUP_FOR_ATTRIBUTES,
SET_ALLOWED_CAPTURE_POLICY,
MOVE_EFFECTS_TO_IO,
SET_RTT_ENABLED
SET_RTT_ENABLED,
SET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY,
REMOVE_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY,
GET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY,
};
#define MAX_ITEMS_PER_LIST 1024
@ -1284,6 +1287,55 @@ public:
}
return static_cast<status_t>(reply.readInt32());
}
virtual status_t setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
data.writeUint32(static_cast<uint32_t>(strategy));
status_t status = device.writeToParcel(&data);
if (status != NO_ERROR) {
return BAD_VALUE;
}
status = remote()->transact(SET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY,
data, &reply);
if (status != NO_ERROR) {
return status;
}
return static_cast<status_t>(reply.readInt32());
}
virtual status_t removePreferredDeviceForStrategy(product_strategy_t strategy)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
data.writeUint32(static_cast<uint32_t>(strategy));
status_t status = remote()->transact(REMOVE_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY,
data, &reply);
if (status != NO_ERROR) {
return status;
}
return static_cast<status_t>(reply.readInt32());
}
virtual status_t getPreferredDeviceForStrategy(product_strategy_t strategy,
AudioDeviceTypeAddr &device)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
data.writeUint32(static_cast<uint32_t>(strategy));
status_t status = remote()->transact(GET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY,
data, &reply);
if (status != NO_ERROR) {
return status;
}
status = device.readFromParcel(&reply);
if (status != NO_ERROR) {
return status;
}
return static_cast<status_t>(reply.readInt32());
}
};
IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService");
@ -1346,7 +1398,10 @@ status_t BnAudioPolicyService::onTransact(
case GET_OFFLOAD_FORMATS_A2DP:
case LIST_AUDIO_VOLUME_GROUPS:
case GET_VOLUME_GROUP_FOR_ATTRIBUTES:
case SET_RTT_ENABLED: {
case SET_RTT_ENABLED:
case SET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY:
case REMOVE_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY:
case GET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY: {
if (!isServiceUid(IPCThreadState::self()->getCallingUid())) {
ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
__func__, code, IPCThreadState::self()->getCallingPid(),
@ -2369,6 +2424,40 @@ status_t BnAudioPolicyService::onTransact(
return NO_ERROR;
}
case SET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
product_strategy_t strategy = (product_strategy_t) data.readUint32();
AudioDeviceTypeAddr device;
status_t status = device.readFromParcel((Parcel*)&data);
if (status != NO_ERROR) {
return status;
}
status = setPreferredDeviceForStrategy(strategy, device);
reply->writeInt32(status);
return NO_ERROR;
}
case REMOVE_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
product_strategy_t strategy = (product_strategy_t) data.readUint32();
status_t status = removePreferredDeviceForStrategy(strategy);
reply->writeInt32(status);
return NO_ERROR;
}
case GET_PREFERRED_DEVICE_FOR_PRODUCT_STRATEGY: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
product_strategy_t strategy = (product_strategy_t) data.readUint32();
AudioDeviceTypeAddr device;
status_t status = getPreferredDeviceForStrategy(strategy, device);
status_t marshall_status = device.writeToParcel(reply);
if (marshall_status != NO_ERROR) {
return marshall_status;
}
reply->writeInt32(status);
return NO_ERROR;
}
default:
return BBinder::onTransact(code, data, reply, flags);
}

@ -403,6 +403,17 @@ public:
*/
static status_t setAudioHalPids(const std::vector<pid_t>& pids);
static status_t setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device);
static status_t removePreferredDeviceForStrategy(product_strategy_t strategy);
static status_t getPreferredDeviceForStrategy(product_strategy_t strategy,
AudioDeviceTypeAddr &device);
static status_t getDeviceForStrategy(product_strategy_t strategy,
AudioDeviceTypeAddr &device);
// ----------------------------------------------------------------------------
class AudioVolumeGroupCallback : public RefBase

@ -223,6 +223,14 @@ public:
volume_group_t &volumeGroup) = 0;
virtual status_t setRttEnabled(bool enabled) = 0;
virtual status_t setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device) = 0;
virtual status_t removePreferredDeviceForStrategy(product_strategy_t strategy) = 0;
virtual status_t getPreferredDeviceForStrategy(product_strategy_t strategy,
AudioDeviceTypeAddr &device) = 0;
};

@ -37,6 +37,8 @@ struct AudioDeviceTypeAddr : public Parcelable {
bool equals(const AudioDeviceTypeAddr& other) const;
AudioDeviceTypeAddr& operator= (const AudioDeviceTypeAddr&) = default;
void reset();
status_t readFromParcel(const Parcel *parcel) override;

@ -271,6 +271,14 @@ public:
virtual status_t getVolumeGroupFromAudioAttributes(const AudioAttributes &aa,
volume_group_t &volumeGroup) = 0;
virtual status_t setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device) = 0;
virtual status_t removePreferredDeviceForStrategy(product_strategy_t strategy) = 0;
virtual status_t getPreferredDeviceForStrategy(product_strategy_t strategy,
AudioDeviceTypeAddr &device) = 0;
};

@ -93,6 +93,13 @@ public:
void dump(String8 *dst) const override;
status_t setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device) override;
status_t removePreferredDeviceForStrategy(product_strategy_t strategy) override;
status_t getPreferredDeviceForStrategy(product_strategy_t strategy,
AudioDeviceTypeAddr &device) const override;
engineConfig::ParsingResult loadAudioPolicyEngineConfig();
@ -124,6 +131,7 @@ private:
AudioPolicyManagerObserver *mApmObserver = nullptr;
ProductStrategyMap mProductStrategies;
ProductStrategyPreferredRoutingMap mProductStrategyPreferredDevices;
VolumeGroupMap mVolumeGroups;
LastRemovableMediaDevices mLastRemovableMediaDevices;
audio_mode_t mPhoneState = AUDIO_MODE_NORMAL; /**< current phone state. */

@ -28,6 +28,7 @@
#include <utils/String8.h>
#include <media/AudioAttributes.h>
#include <media/AudioContainers.h>
#include <media/AudioPolicy.h>
namespace android {
@ -163,4 +164,10 @@ private:
product_strategy_t mDefaultStrategy = PRODUCT_STRATEGY_NONE;
};
class ProductStrategyPreferredRoutingMap : public std::map<product_strategy_t, AudioDeviceTypeAddr>
{
public:
void dump(String8 *dst, int spaces = 0) const;
};
} // namespace android

@ -283,9 +283,57 @@ status_t EngineBase::listAudioVolumeGroups(AudioVolumeGroupVector &groups) const
return NO_ERROR;
}
status_t EngineBase::setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device)
{
// verify strategy exists
if (mProductStrategies.find(strategy) == mProductStrategies.end()) {
ALOGE("%s invalid strategy %u", __func__, strategy);
return BAD_VALUE;
}
mProductStrategyPreferredDevices[strategy] = device;
return NO_ERROR;
}
status_t EngineBase::removePreferredDeviceForStrategy(product_strategy_t strategy)
{
// verify strategy exists
if (mProductStrategies.find(strategy) == mProductStrategies.end()) {
ALOGE("%s invalid strategy %u", __func__, strategy);
return BAD_VALUE;
}
if (mProductStrategyPreferredDevices.erase(strategy) == 0) {
// no preferred device was set
return NAME_NOT_FOUND;
}
return NO_ERROR;
}
status_t EngineBase::getPreferredDeviceForStrategy(product_strategy_t strategy,
AudioDeviceTypeAddr &device) const
{
// verify strategy exists
if (mProductStrategies.find(strategy) == mProductStrategies.end()) {
ALOGE("%s unknown strategy %u", __func__, strategy);
return BAD_VALUE;
}
// preferred device for this strategy?
auto devIt = mProductStrategyPreferredDevices.find(strategy);
if (devIt == mProductStrategyPreferredDevices.end()) {
ALOGV("%s no preferred device for strategy %u", __func__, strategy);
return NAME_NOT_FOUND;
}
device = devIt->second;
return NO_ERROR;
}
void EngineBase::dump(String8 *dst) const
{
mProductStrategies.dump(dst, 2);
mProductStrategyPreferredDevices.dump(dst, 2);
mVolumeGroups.dump(dst, 2);
}

@ -310,5 +310,15 @@ void ProductStrategyMap::dump(String8 *dst, int spaces) const
}
}
void ProductStrategyPreferredRoutingMap::dump(android::String8* dst, int spaces) const {
dst->appendFormat("\n%*sPreferred devices per product strategy dump:", spaces, "");
for (const auto& iter : *this) {
dst->appendFormat("\n%*sStrategy %u dev:%08x addr:%s",
spaces + 2, "",
(uint32_t) iter.first,
iter.second.mType, iter.second.mAddress.c_str());
}
dst->appendFormat("\n");
}
}

@ -292,6 +292,39 @@ public:
*/
virtual status_t listAudioVolumeGroups(AudioVolumeGroupVector &groups) const = 0;
/**
* @brief setPreferredDeviceForStrategy sets the default device to be used for a
* strategy when available
* @param strategy the audio strategy whose routing will be affected
* @param device the audio device to route to when available
* @return BAD_VALUE if the strategy is invalid,
* or NO_ERROR if the preferred device was set
*/
virtual status_t setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device) = 0;
/**
* @brief removePreferredDeviceForStrategy removes the preferred device previously set
* for the given strategy
* @param strategy the audio strategy whose routing will be affected
* @return BAD_VALUE if the strategy is invalid,
* or NO_ERROR if the preferred device was removed
*/
virtual status_t removePreferredDeviceForStrategy(product_strategy_t strategy) = 0;
/**
* @brief getPreferredDeviceForStrategy queries which device is set as the
* preferred device for the given strategy
* @param strategy the strategy to query
* @param device returns configured as the preferred device if one was set
* @return BAD_VALUE if the strategy is invalid,
* or NAME_NOT_FOUND if no preferred device was set
* or NO_ERROR if the device parameter was initialized to the preferred device
*/
virtual status_t getPreferredDeviceForStrategy(product_strategy_t strategy,
AudioDeviceTypeAddr &device) const = 0;
virtual void dump(String8 *dst) const = 0;
protected:

@ -604,7 +604,7 @@ sp<DeviceDescriptor> Engine::getDeviceForInputSource(audio_source_t inputSource)
void Engine::updateDeviceSelectionCache()
{
for (const auto &iter : getProductStrategies()) {
const auto &strategy = iter.second;
const auto& strategy = iter.second;
auto devices = getDevicesForProductStrategy(strategy->getId());
mDevicesForStrategies[strategy->getId()] = devices;
strategy->setDeviceTypes(devices.types());
@ -612,14 +612,30 @@ void Engine::updateDeviceSelectionCache()
}
}
DeviceVector Engine::getDevicesForProductStrategy(product_strategy_t strategy) const
{
DeviceVector Engine::getDevicesForProductStrategy(product_strategy_t strategy) const {
DeviceVector availableOutputDevices = getApmObserver()->getAvailableOutputDevices();
DeviceVector availableInputDevices = getApmObserver()->getAvailableInputDevices();
const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs();
// check if this strategy has a preferred device that is available,
// if yes, give priority to it
AudioDeviceTypeAddr preferredStrategyDevice;
const status_t status = getPreferredDeviceForStrategy(strategy, preferredStrategyDevice);
if (status == NO_ERROR) {
// there is a preferred device, is it available?
sp<DeviceDescriptor> preferredAvailableDevDescr = availableOutputDevices.getDevice(
preferredStrategyDevice.mType,
String8(preferredStrategyDevice.mAddress.c_str()),
AUDIO_FORMAT_DEFAULT);
if (preferredAvailableDevDescr != nullptr) {
ALOGVV("%s using pref device 0x%08x/%s for strategy %u", __FUNCTION__,
preferredStrategyDevice.mType, preferredStrategyDevice.mAddress, strategy);
return DeviceVector(preferredAvailableDevDescr);
}
}
DeviceVector availableInputDevices = getApmObserver()->getAvailableInputDevices();
const SwAudioOutputCollection& outputs = getApmObserver()->getOutputs();
auto legacyStrategy = mLegacyStrategyMap.find(strategy) != end(mLegacyStrategyMap) ?
mLegacyStrategyMap.at(strategy) : STRATEGY_NONE;
mLegacyStrategyMap.at(strategy) : STRATEGY_NONE;
return getDevicesForStrategyInt(legacyStrategy,
availableOutputDevices,
availableInputDevices, outputs);

@ -794,27 +794,11 @@ void AudioPolicyManager::setForceUse(audio_policy_force_use_t usage,
//FIXME: workaround for truncated touch sounds
// to be removed when the problem is handled by system UI
uint32_t delayMs = 0;
uint32_t waitMs = 0;
if (usage == AUDIO_POLICY_FORCE_FOR_COMMUNICATION) {
delayMs = TOUCH_SOUND_FIXED_DELAY_MS;
}
if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
DeviceVector newDevices = getNewOutputDevices(mPrimaryOutput, true /*fromCache*/);
waitMs = updateCallRouting(newDevices, delayMs);
}
for (size_t i = 0; i < mOutputs.size(); i++) {
sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(i);
DeviceVector newDevices = getNewOutputDevices(outputDesc, true /*fromCache*/);
if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (outputDesc != mPrimaryOutput)) {
// As done in setDeviceConnectionState, we could also fix default device issue by
// preventing the force re-routing in case of default dev that distinguishes on address.
// Let's give back to engine full device choice decision however.
waitMs = setOutputDevices(outputDesc, newDevices, !newDevices.isEmpty(), delayMs);
}
if (forceVolumeReeval && !newDevices.isEmpty()) {
applyStreamVolumes(outputDesc, newDevices.types(), waitMs, true);
}
}
updateCallAndOutputRouting(forceVolumeReeval, delayMs);
for (const auto& activeDesc : mInputs.getActiveInputs()) {
auto newDevice = getNewInputDevice(activeDesc);
@ -3084,6 +3068,72 @@ status_t AudioPolicyManager::removeUidDeviceAffinities(uid_t uid) {
return res;
}
status_t AudioPolicyManager::setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device) {
ALOGI("%s() strategy=%d device=%08x addr=%s", __FUNCTION__,
strategy, device.mType, device.mAddress.c_str());
// strategy preferred device is only for output devices
if (!audio_is_output_device(device.mType)) {
ALOGE("%s() device=%08x is NOT an output device", __FUNCTION__, device.mType);
return BAD_VALUE;
}
status_t status = mEngine->setPreferredDeviceForStrategy(strategy, device);
if (status != NO_ERROR) {
ALOGW("Engine could not set preferred device %08x %s for strategy %d",
device.mType, device.mAddress.c_str(), strategy);
return status;
}
checkForDeviceAndOutputChanges();
updateCallAndOutputRouting();
return NO_ERROR;
}
void AudioPolicyManager::updateCallAndOutputRouting(bool forceVolumeReeval, uint32_t delayMs)
{
uint32_t waitMs = 0;
if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
DeviceVector newDevices = getNewOutputDevices(mPrimaryOutput, true /*fromCache*/);
waitMs = updateCallRouting(newDevices, delayMs);
}
for (size_t i = 0; i < mOutputs.size(); i++) {
sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(i);
DeviceVector newDevices = getNewOutputDevices(outputDesc, true /*fromCache*/);
if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (outputDesc != mPrimaryOutput)) {
// As done in setDeviceConnectionState, we could also fix default device issue by
// preventing the force re-routing in case of default dev that distinguishes on address.
// Let's give back to engine full device choice decision however.
waitMs = setOutputDevices(outputDesc, newDevices, !newDevices.isEmpty(), delayMs);
}
if (forceVolumeReeval && !newDevices.isEmpty()) {
applyStreamVolumes(outputDesc, newDevices.types(), waitMs, true);
}
}
}
status_t AudioPolicyManager::removePreferredDeviceForStrategy(product_strategy_t strategy)
{
ALOGI("%s() strategy=%d", __FUNCTION__, strategy);
status_t status = mEngine->removePreferredDeviceForStrategy(strategy);
if (status != NO_ERROR) {
ALOGW("Engine could not remove preferred device for strategy %d", strategy);
return status;
}
checkForDeviceAndOutputChanges();
updateCallAndOutputRouting();
return NO_ERROR;
}
status_t AudioPolicyManager::getPreferredDeviceForStrategy(product_strategy_t strategy,
AudioDeviceTypeAddr &device) {
return mEngine->getPreferredDeviceForStrategy(strategy, device);
}
void AudioPolicyManager::dump(String8 *dst) const
{
dst->appendFormat("\nAudioPolicyManager Dump: %p\n", this);
@ -5056,6 +5106,7 @@ void AudioPolicyManager::checkOutputForAttributes(const audio_attributes_t &attr
DeviceVector oldDevices = mEngine->getOutputDevicesForAttributes(attr, 0, true /*fromCache*/);
DeviceVector newDevices = mEngine->getOutputDevicesForAttributes(attr, 0, false /*fromCache*/);
SortedVector<audio_io_handle_t> srcOutputs = getOutputsForDevices(oldDevices, mPreviousOutputs);
SortedVector<audio_io_handle_t> dstOutputs = getOutputsForDevices(newDevices, mOutputs);

@ -258,6 +258,12 @@ public:
const Vector<AudioDeviceTypeAddr>& devices);
virtual status_t removeUidDeviceAffinities(uid_t uid);
virtual status_t setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device);
virtual status_t removePreferredDeviceForStrategy(product_strategy_t strategy);
virtual status_t getPreferredDeviceForStrategy(product_strategy_t strategy,
AudioDeviceTypeAddr &device);
virtual status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
audio_port_handle_t *portId,
@ -496,12 +502,18 @@ protected:
// close an input.
void closeInput(audio_io_handle_t input);
// runs all the checks required for accomodating changes in devices and outputs
// runs all the checks required for accommodating changes in devices and outputs
// if 'onOutputsChecked' callback is provided, it is executed after the outputs
// check via 'checkOutputForAllStrategies'. If the callback returns 'true',
// A2DP suspend status is rechecked.
void checkForDeviceAndOutputChanges(std::function<bool()> onOutputsChecked = nullptr);
/**
* @brief updates routing for all outputs (including call if call in progress).
* @param delayMs delay for unmuting if required
*/
void updateCallAndOutputRouting(bool forceVolumeReeval = true, uint32_t delayMs = 0);
/**
* @brief checkOutputForAttributes checks and if necessary changes outputs used for the
* given audio attributes.

@ -1325,4 +1325,33 @@ status_t AudioPolicyService::setRttEnabled(bool enabled)
return NO_ERROR;
}
status_t AudioPolicyService::setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device)
{
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
Mutex::Autolock _l(mLock);
return mAudioPolicyManager->setPreferredDeviceForStrategy(strategy, device);
}
status_t AudioPolicyService::removePreferredDeviceForStrategy(product_strategy_t strategy)
{
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
Mutex::Autolock _l(mLock);
return mAudioPolicyManager->removePreferredDeviceForStrategy(strategy);
}
status_t AudioPolicyService::getPreferredDeviceForStrategy(product_strategy_t strategy,
AudioDeviceTypeAddr &device)
{
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
Mutex::Autolock _l(mLock);
return mAudioPolicyManager->getPreferredDeviceForStrategy(strategy, device);
}
} // namespace android

@ -224,6 +224,15 @@ public:
virtual status_t removeUidDeviceAffinities(uid_t uid);
virtual status_t setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device);
virtual status_t removePreferredDeviceForStrategy(product_strategy_t strategy);
virtual status_t getPreferredDeviceForStrategy(product_strategy_t strategy,
AudioDeviceTypeAddr &device);
virtual status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
audio_port_handle_t *portId);

Loading…
Cancel
Save