audio: Allow Stream SM to stay in DRAINING state for early notify
Since for the "early notify" drain the stream sends `onDrainReady`
sligtly before the draining has actually finished, it should be
allowed to stay in the `DRAINING` state while sending this
notification. The stream gets into the `IDLE` state once the drain
has completed. While draining is ongoing, it is allowed for
the client to pause the stream.
As this is a new behavior in audio.core V3, extend VTS to skip
tests that verify this behavior for previous audio HAL versions.
Also, add previously missed `DrainOutAsync` scenario to the tests
list.
Bug: 363958142
Test: atest VtsHalAudioCoreTargetTest
Change-Id: I6878b07c2fa1ce4f19b3c9c5d0e0f2e5288e55c4
diff --git a/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv b/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv
index 56b7926..e2da90d 100644
--- a/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv
+++ b/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv
@@ -45,6 +45,8 @@
PAUSED -> ACTIVE [label="start"]; // consumer -> active
PAUSED -> IDLE [label="flush"]; // producer -> passive, buffer is cleared
DRAINING -> IDLE [label="←IStreamCallback.onDrainReady"];
+ DRAINING -> DRAINING [label="←IStreamCallback.onDrainReady"]; // allowed for `DRAIN_EARLY_NOTIFY`
+ DRAINING -> IDLE [label="<empty buffer>"]; // allowed for `DRAIN_EARLY_NOTIFY`
DRAINING -> TRANSFERRING [label="burst"]; // producer -> active
DRAINING -> ACTIVE [label="burst"]; // full write
DRAINING -> DRAIN_PAUSED [label="pause"]; // consumer -> passive (not consuming)
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index 51b6085..e96cf81 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -207,9 +207,9 @@
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
const auto& flags = portConfigIt->flags.value();
- StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs,
- mVendorDebug.forceTransientBurst,
- mVendorDebug.forceSynchronousDrain};
+ StreamContext::DebugParameters params{
+ mDebug.streamTransientStateDelayMs, mVendorDebug.forceTransientBurst,
+ mVendorDebug.forceSynchronousDrain, mVendorDebug.forceDrainToDraining};
std::unique_ptr<StreamContext::DataMQ> dataMQ = nullptr;
std::shared_ptr<IStreamCallback> streamAsyncCallback = nullptr;
std::shared_ptr<ISoundDose> soundDose;
@@ -1524,6 +1524,7 @@
const std::string Module::VendorDebug::kForceTransientBurstName = "aosp.forceTransientBurst";
const std::string Module::VendorDebug::kForceSynchronousDrainName = "aosp.forceSynchronousDrain";
+const std::string Module::VendorDebug::kForceDrainToDrainingName = "aosp.forceDrainToDraining";
ndk::ScopedAStatus Module::getVendorParameters(const std::vector<std::string>& in_ids,
std::vector<VendorParameter>* _aidl_return) {
@@ -1538,6 +1539,10 @@
VendorParameter forceSynchronousDrain{.id = id};
forceSynchronousDrain.ext.setParcelable(Boolean{mVendorDebug.forceSynchronousDrain});
_aidl_return->push_back(std::move(forceSynchronousDrain));
+ } else if (id == VendorDebug::kForceDrainToDrainingName) {
+ VendorParameter forceDrainToDraining{.id = id};
+ forceDrainToDraining.ext.setParcelable(Boolean{mVendorDebug.forceDrainToDraining});
+ _aidl_return->push_back(std::move(forceDrainToDraining));
} else {
allParametersKnown = false;
LOG(VERBOSE) << __func__ << ": " << mType << ": unrecognized parameter \"" << id << "\"";
@@ -1578,6 +1583,10 @@
if (!extractParameter<Boolean>(p, &mVendorDebug.forceSynchronousDrain)) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
+ } else if (p.id == VendorDebug::kForceDrainToDrainingName) {
+ if (!extractParameter<Boolean>(p, &mVendorDebug.forceDrainToDraining)) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
} else {
allParametersKnown = false;
LOG(VERBOSE) << __func__ << ": " << mType << ": unrecognized parameter \"" << p.id
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index 3d7f30c..4525f6a 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -382,8 +382,20 @@
const std::string StreamOutWorkerLogic::kThreadName = "writer";
StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
- if (mState == StreamDescriptor::State::DRAINING ||
- mState == StreamDescriptor::State::TRANSFERRING) {
+ if (mState == StreamDescriptor::State::DRAINING && mContext->getForceDrainToDraining() &&
+ mOnDrainReadyStatus == OnDrainReadyStatus::UNSENT) {
+ std::shared_ptr<IStreamCallback> asyncCallback = mContext->getAsyncCallback();
+ if (asyncCallback != nullptr) {
+ ndk::ScopedAStatus status = asyncCallback->onDrainReady();
+ if (!status.isOk()) {
+ LOG(ERROR) << __func__ << ": error from onDrainReady: " << status;
+ }
+ // This sets the timeout for moving into IDLE on next iterations.
+ switchToTransientState(StreamDescriptor::State::DRAINING);
+ mOnDrainReadyStatus = OnDrainReadyStatus::SENT;
+ }
+ } else if (mState == StreamDescriptor::State::DRAINING ||
+ mState == StreamDescriptor::State::TRANSFERRING) {
if (auto stateDurationMs = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - mTransientStateStart);
stateDurationMs >= mTransientStateDelayMs) {
@@ -396,9 +408,12 @@
// drain or transfer completion. In the stub, we switch unconditionally.
if (mState == StreamDescriptor::State::DRAINING) {
mState = StreamDescriptor::State::IDLE;
- ndk::ScopedAStatus status = asyncCallback->onDrainReady();
- if (!status.isOk()) {
- LOG(ERROR) << __func__ << ": error from onDrainReady: " << status;
+ if (mOnDrainReadyStatus != OnDrainReadyStatus::SENT) {
+ ndk::ScopedAStatus status = asyncCallback->onDrainReady();
+ if (!status.isOk()) {
+ LOG(ERROR) << __func__ << ": error from onDrainReady: " << status;
+ }
+ mOnDrainReadyStatus = OnDrainReadyStatus::SENT;
}
} else {
mState = StreamDescriptor::State::ACTIVE;
@@ -537,6 +552,10 @@
mState = StreamDescriptor::State::IDLE;
} else {
switchToTransientState(StreamDescriptor::State::DRAINING);
+ mOnDrainReadyStatus =
+ mode == StreamDescriptor::DrainMode::DRAIN_EARLY_NOTIFY
+ ? OnDrainReadyStatus::UNSENT
+ : OnDrainReadyStatus::IGNORE;
}
} else {
LOG(ERROR) << __func__ << ": drain failed: " << status;
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index 7e32cf2..d03598a 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -148,8 +148,10 @@
struct VendorDebug {
static const std::string kForceTransientBurstName;
static const std::string kForceSynchronousDrainName;
+ static const std::string kForceDrainToDrainingName;
bool forceTransientBurst = false;
bool forceSynchronousDrain = false;
+ bool forceDrainToDraining = false;
};
// ids of device ports created at runtime via 'connectExternalDevice'.
// Also stores a list of ids of mix ports with dynamic profiles that were populated from
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index f7b9269..8297fc5 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -78,6 +78,10 @@
bool forceTransientBurst = false;
// Force the "drain" command to be synchronous, going directly to the IDLE state.
bool forceSynchronousDrain = false;
+ // Force the "drain early notify" command to keep the SM in the DRAINING state
+ // after sending 'onDrainReady' callback. The SM moves to IDLE after
+ // 'transientStateDelayMs'.
+ bool forceDrainToDraining = false;
};
StreamContext() = default;
@@ -119,6 +123,7 @@
::aidl::android::media::audio::common::AudioIoFlags getFlags() const { return mFlags; }
bool getForceTransientBurst() const { return mDebugParameters.forceTransientBurst; }
bool getForceSynchronousDrain() const { return mDebugParameters.forceSynchronousDrain; }
+ bool getForceDrainToDraining() const { return mDebugParameters.forceDrainToDraining; }
size_t getFrameSize() const;
int getInternalCommandCookie() const { return mInternalCommandCookie; }
int32_t getMixPortHandle() const { return mMixPortHandle; }
@@ -301,6 +306,9 @@
bool write(size_t clientSize, StreamDescriptor::Reply* reply);
std::shared_ptr<IStreamOutEventCallback> mEventCallback;
+
+ enum OnDrainReadyStatus : int32_t { IGNORE /*used for DRAIN_ALL*/, UNSENT, SENT };
+ OnDrainReadyStatus mOnDrainReadyStatus = OnDrainReadyStatus::IGNORE;
};
using StreamOutWorker = StreamWorkerImpl<StreamOutWorkerLogic>;
diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
index 6bfba65..6bce107 100644
--- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
@@ -117,6 +117,10 @@
using ndk::enum_range;
using ndk::ScopedAStatus;
+static constexpr int32_t kAidlVersion1 = 1;
+static constexpr int32_t kAidlVersion2 = 2;
+static constexpr int32_t kAidlVersion3 = 3;
+
template <typename T>
std::set<int32_t> extractIds(const std::vector<T>& v) {
std::set<int32_t> ids;
@@ -452,7 +456,6 @@
// This is implemented by the 'StreamFixture' utility class.
static constexpr int kNegativeTestBufferSizeFrames = 256;
static constexpr int kDefaultLargeBufferSizeFrames = 48000;
- static constexpr int32_t kAidlVersion3 = 3;
void SetUpImpl(const std::string& moduleName, bool setUpDebug = true) {
ASSERT_NO_FATAL_FAILURE(ConnectToService(moduleName, setUpDebug));
@@ -582,7 +585,7 @@
std::unique_ptr<WithDebugFlags> debug;
std::vector<AudioPort> initialPorts;
std::vector<AudioRoute> initialRoutes;
- int32_t aidlVersion;
+ int32_t aidlVersion = -1;
};
class WithDevicePortConnectedState {
@@ -1837,6 +1840,7 @@
}
TEST_P(AudioCoreModule, SetAudioPortConfigInvalidPortAudioGain) {
+ ASSERT_GE(aidlVersion, kAidlVersion1);
if (aidlVersion < kAidlVersion3) {
GTEST_SKIP() << "Skip for audio HAL version lower than " << kAidlVersion3;
}
@@ -4021,6 +4025,7 @@
enum {
NAMED_CMD_NAME,
+ NAMED_CMD_MIN_INTERFACE_VERSION,
NAMED_CMD_DELAY_MS,
NAMED_CMD_STREAM_TYPE,
NAMED_CMD_CMDS,
@@ -4028,7 +4033,7 @@
};
enum class StreamTypeFilter { ANY, SYNC, ASYNC };
using NamedCommandSequence =
- std::tuple<std::string, int /*cmdDelayMs*/, StreamTypeFilter,
+ std::tuple<std::string, int /*minInterfaceVersion*/, int /*cmdDelayMs*/, StreamTypeFilter,
std::shared_ptr<StateSequence>, bool /*validatePositionIncrease*/>;
enum { PARAM_MODULE_NAME, PARAM_CMD_SEQ, PARAM_SETUP_SEQ };
using StreamIoTestParameters =
@@ -4039,6 +4044,12 @@
public:
void SetUp() override {
ASSERT_NO_FATAL_FAILURE(SetUpImpl(std::get<PARAM_MODULE_NAME>(GetParam())));
+ ASSERT_GE(aidlVersion, kAidlVersion1);
+ if (const int minVersion =
+ std::get<NAMED_CMD_MIN_INTERFACE_VERSION>(std::get<PARAM_CMD_SEQ>(GetParam()));
+ aidlVersion < minVersion) {
+ GTEST_SKIP() << "Skip for audio HAL version lower than " << minVersion;
+ }
ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
}
@@ -4048,6 +4059,20 @@
if (allPortConfigs.empty()) {
GTEST_SKIP() << "No mix ports have attached devices";
}
+ const auto& commandsAndStates =
+ std::get<NAMED_CMD_CMDS>(std::get<PARAM_CMD_SEQ>(GetParam()));
+ const bool validatePositionIncrease =
+ std::get<NAMED_CMD_VALIDATE_POS_INCREASE>(std::get<PARAM_CMD_SEQ>(GetParam()));
+ auto runStreamIoCommands = [&](const AudioPortConfig& portConfig) {
+ if (!std::get<PARAM_SETUP_SEQ>(GetParam())) {
+ ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq1(portConfig, commandsAndStates,
+ validatePositionIncrease));
+ } else {
+ ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq2(portConfig, commandsAndStates,
+ validatePositionIncrease));
+ }
+ };
+
for (const auto& portConfig : allPortConfigs) {
auto port = moduleConfig->getPort(portConfig.portId);
ASSERT_TRUE(port.has_value());
@@ -4075,16 +4100,18 @@
delayTransientStates.flags().streamTransientStateDelayMs =
std::get<NAMED_CMD_DELAY_MS>(std::get<PARAM_CMD_SEQ>(GetParam()));
ASSERT_NO_FATAL_FAILURE(delayTransientStates.SetUp(module.get()));
- const auto& commandsAndStates =
- std::get<NAMED_CMD_CMDS>(std::get<PARAM_CMD_SEQ>(GetParam()));
- const bool validatePositionIncrease =
- std::get<NAMED_CMD_VALIDATE_POS_INCREASE>(std::get<PARAM_CMD_SEQ>(GetParam()));
- if (!std::get<PARAM_SETUP_SEQ>(GetParam())) {
- ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq1(portConfig, commandsAndStates,
- validatePositionIncrease));
- } else {
- ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq2(portConfig, commandsAndStates,
- validatePositionIncrease));
+ ASSERT_NO_FATAL_FAILURE(runStreamIoCommands(portConfig));
+ if (aidlVersion >= kAidlVersion3 && isNonBlocking && !IOTraits<Stream>::is_input) {
+ // Also try running the same sequence with "aosp.forceDrainToDraining" set.
+ // This will only work with the default implementation. When it works, the stream
+ // tries always to move to the 'DRAINING' state after an "early notify" drain.
+ // This helps to check more paths for our test scenarios.
+ WithModuleParameter forceDrainToDraining("aosp.forceDrainToDraining",
+ Boolean{true});
+ if (forceDrainToDraining.SetUpNoChecks(module.get(), true /*failureExpected*/)
+ .isOk()) {
+ ASSERT_NO_FATAL_FAILURE(runStreamIoCommands(portConfig));
+ }
}
if (isNonBlocking) {
// Also try running the same sequence with "aosp.forceTransientBurst" set.
@@ -4094,13 +4121,7 @@
WithModuleParameter forceTransientBurst("aosp.forceTransientBurst", Boolean{true});
if (forceTransientBurst.SetUpNoChecks(module.get(), true /*failureExpected*/)
.isOk()) {
- if (!std::get<PARAM_SETUP_SEQ>(GetParam())) {
- ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq1(
- portConfig, commandsAndStates, validatePositionIncrease));
- } else {
- ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq2(
- portConfig, commandsAndStates, validatePositionIncrease));
- }
+ ASSERT_NO_FATAL_FAILURE(runStreamIoCommands(portConfig));
}
} else if (!IOTraits<Stream>::is_input) {
// Also try running the same sequence with "aosp.forceSynchronousDrain" set.
@@ -4111,13 +4132,7 @@
Boolean{true});
if (forceSynchronousDrain.SetUpNoChecks(module.get(), true /*failureExpected*/)
.isOk()) {
- if (!std::get<PARAM_SETUP_SEQ>(GetParam())) {
- ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq1(
- portConfig, commandsAndStates, validatePositionIncrease));
- } else {
- ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq2(
- portConfig, commandsAndStates, validatePositionIncrease));
- }
+ ASSERT_NO_FATAL_FAILURE(runStreamIoCommands(portConfig));
}
}
}
@@ -4570,14 +4585,14 @@
return std::make_shared<StateSequenceFollower>(std::move(d));
}
static const NamedCommandSequence kReadSeq =
- std::make_tuple(std::string("Read"), 0, StreamTypeFilter::ANY, makeBurstCommands(true),
- true /*validatePositionIncrease*/);
+ std::make_tuple(std::string("Read"), kAidlVersion1, 0, StreamTypeFilter::ANY,
+ makeBurstCommands(true), true /*validatePositionIncrease*/);
static const NamedCommandSequence kWriteSyncSeq =
- std::make_tuple(std::string("Write"), 0, StreamTypeFilter::SYNC, makeBurstCommands(true),
- true /*validatePositionIncrease*/);
+ std::make_tuple(std::string("Write"), kAidlVersion1, 0, StreamTypeFilter::SYNC,
+ makeBurstCommands(true), true /*validatePositionIncrease*/);
static const NamedCommandSequence kWriteAsyncSeq =
- std::make_tuple(std::string("Write"), 0, StreamTypeFilter::ASYNC, makeBurstCommands(false),
- true /*validatePositionIncrease*/);
+ std::make_tuple(std::string("Write"), kAidlVersion1, 0, StreamTypeFilter::ASYNC,
+ makeBurstCommands(false), true /*validatePositionIncrease*/);
std::shared_ptr<StateSequence> makeAsyncDrainCommands(bool isInput) {
using State = StreamDescriptor::State;
@@ -4606,10 +4621,10 @@
return std::make_shared<StateSequenceFollower>(std::move(d));
}
static const NamedCommandSequence kWriteDrainAsyncSeq = std::make_tuple(
- std::string("WriteDrain"), kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
- makeAsyncDrainCommands(false), false /*validatePositionIncrease*/);
+ std::string("WriteDrain"), kAidlVersion1, kStreamTransientStateTransitionDelayMs,
+ StreamTypeFilter::ASYNC, makeAsyncDrainCommands(false), false /*validatePositionIncrease*/);
static const NamedCommandSequence kDrainInSeq =
- std::make_tuple(std::string("Drain"), 0, StreamTypeFilter::ANY,
+ std::make_tuple(std::string("Drain"), kAidlVersion1, 0, StreamTypeFilter::ANY,
makeAsyncDrainCommands(true), false /*validatePositionIncrease*/);
std::shared_ptr<StateSequence> makeDrainOutCommands(bool isSync) {
@@ -4631,12 +4646,28 @@
return std::make_shared<StateSequenceFollower>(std::move(d));
}
static const NamedCommandSequence kDrainOutSyncSeq =
- std::make_tuple(std::string("Drain"), 0, StreamTypeFilter::SYNC, makeDrainOutCommands(true),
- false /*validatePositionIncrease*/);
+ std::make_tuple(std::string("Drain"), kAidlVersion1, 0, StreamTypeFilter::SYNC,
+ makeDrainOutCommands(true), false /*validatePositionIncrease*/);
static const NamedCommandSequence kDrainOutAsyncSeq =
- std::make_tuple(std::string("Drain"), 0, StreamTypeFilter::ASYNC,
+ std::make_tuple(std::string("Drain"), kAidlVersion3, 0, StreamTypeFilter::ASYNC,
makeDrainOutCommands(false), false /*validatePositionIncrease*/);
+std::shared_ptr<StateSequence> makeDrainEarlyOutCommands() {
+ using State = StreamDescriptor::State;
+ auto d = std::make_unique<StateDag>();
+ StateDag::Node last = d->makeFinalNode(State::IDLE);
+ StateDag::Node draining = d->makeNode(State::DRAINING, kDrainReadyEvent, last);
+ draining.children().push_back(d->makeNode(State::DRAINING, kGetStatusCommand, last));
+ StateDag::Node active = d->makeNode(State::ACTIVE, kDrainOutEarlyCommand, draining);
+ StateDag::Node idle = d->makeNode(State::IDLE, kBurstCommand, active);
+ idle.children().push_back(d->makeNode(State::TRANSFERRING, kTransferReadyEvent, active));
+ d->makeNode(State::STANDBY, kStartCommand, idle);
+ return std::make_shared<StateSequenceFollower>(std::move(d));
+}
+static const NamedCommandSequence kDrainEarlyOutAsyncSeq =
+ std::make_tuple(std::string("DrainEarly"), kAidlVersion3, 0, StreamTypeFilter::ASYNC,
+ makeDrainEarlyOutCommands(), false /*validatePositionIncrease*/);
+
std::shared_ptr<StateSequence> makeDrainPauseOutCommands(bool isSync) {
using State = StreamDescriptor::State;
auto d = std::make_unique<StateDag>();
@@ -4656,12 +4687,33 @@
d->makeNode(State::STANDBY, kStartCommand, idle);
return std::make_shared<StateSequenceFollower>(std::move(d));
}
-static const NamedCommandSequence kDrainPauseOutSyncSeq = std::make_tuple(
- std::string("DrainPause"), kStreamTransientStateTransitionDelayMs, StreamTypeFilter::SYNC,
- makeDrainPauseOutCommands(true), false /*validatePositionIncrease*/);
-static const NamedCommandSequence kDrainPauseOutAsyncSeq = std::make_tuple(
- std::string("DrainPause"), kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
- makeDrainPauseOutCommands(false), false /*validatePositionIncrease*/);
+static const NamedCommandSequence kDrainPauseOutSyncSeq =
+ std::make_tuple(std::string("DrainPause"), kAidlVersion1,
+ kStreamTransientStateTransitionDelayMs, StreamTypeFilter::SYNC,
+ makeDrainPauseOutCommands(true), false /*validatePositionIncrease*/);
+static const NamedCommandSequence kDrainPauseOutAsyncSeq =
+ std::make_tuple(std::string("DrainPause"), kAidlVersion1,
+ kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
+ makeDrainPauseOutCommands(false), false /*validatePositionIncrease*/);
+
+std::shared_ptr<StateSequence> makeDrainEarlyPauseOutCommands() {
+ using State = StreamDescriptor::State;
+ auto d = std::make_unique<StateDag>();
+ StateDag::Node draining = d->makeNodes({std::make_pair(State::DRAINING, kPauseCommand),
+ std::make_pair(State::DRAIN_PAUSED, kStartCommand),
+ std::make_pair(State::DRAINING, kPauseCommand),
+ std::make_pair(State::DRAIN_PAUSED, kBurstCommand)},
+ State::TRANSFER_PAUSED);
+ StateDag::Node active = d->makeNode(State::ACTIVE, kDrainOutEarlyCommand, draining);
+ StateDag::Node idle = d->makeNode(State::IDLE, kBurstCommand, active);
+ idle.children().push_back(d->makeNode(State::TRANSFERRING, kDrainOutEarlyCommand, draining));
+ d->makeNode(State::STANDBY, kStartCommand, idle);
+ return std::make_shared<StateSequenceFollower>(std::move(d));
+}
+static const NamedCommandSequence kDrainEarlyPauseOutAsyncSeq =
+ std::make_tuple(std::string("DrainEarlyPause"), kAidlVersion3,
+ kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
+ makeDrainEarlyPauseOutCommands(), false /*validatePositionIncrease*/);
// This sequence also verifies that the capture / presentation position is not reset on standby.
std::shared_ptr<StateSequence> makeStandbyCommands(bool isInput, bool isSync) {
@@ -4703,14 +4755,15 @@
return std::make_shared<StateSequenceFollower>(std::move(d));
}
static const NamedCommandSequence kStandbyInSeq =
- std::make_tuple(std::string("Standby"), 0, StreamTypeFilter::ANY,
+ std::make_tuple(std::string("Standby"), kAidlVersion1, 0, StreamTypeFilter::ANY,
makeStandbyCommands(true, false), false /*validatePositionIncrease*/);
static const NamedCommandSequence kStandbyOutSyncSeq =
- std::make_tuple(std::string("Standby"), 0, StreamTypeFilter::SYNC,
+ std::make_tuple(std::string("Standby"), kAidlVersion1, 0, StreamTypeFilter::SYNC,
makeStandbyCommands(false, true), false /*validatePositionIncrease*/);
-static const NamedCommandSequence kStandbyOutAsyncSeq = std::make_tuple(
- std::string("Standby"), kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
- makeStandbyCommands(false, false), false /*validatePositionIncrease*/);
+static const NamedCommandSequence kStandbyOutAsyncSeq =
+ std::make_tuple(std::string("Standby"), kAidlVersion1,
+ kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
+ makeStandbyCommands(false, false), false /*validatePositionIncrease*/);
std::shared_ptr<StateSequence> makePauseCommands(bool isInput, bool isSync) {
using State = StreamDescriptor::State;
@@ -4745,14 +4798,15 @@
return std::make_shared<StateSequenceFollower>(std::move(d));
}
static const NamedCommandSequence kPauseInSeq =
- std::make_tuple(std::string("Pause"), 0, StreamTypeFilter::ANY,
+ std::make_tuple(std::string("Pause"), kAidlVersion1, 0, StreamTypeFilter::ANY,
makePauseCommands(true, false), false /*validatePositionIncrease*/);
static const NamedCommandSequence kPauseOutSyncSeq =
- std::make_tuple(std::string("Pause"), 0, StreamTypeFilter::SYNC,
+ std::make_tuple(std::string("Pause"), kAidlVersion1, 0, StreamTypeFilter::SYNC,
makePauseCommands(false, true), false /*validatePositionIncrease*/);
-static const NamedCommandSequence kPauseOutAsyncSeq = std::make_tuple(
- std::string("Pause"), kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
- makePauseCommands(false, false), false /*validatePositionIncrease*/);
+static const NamedCommandSequence kPauseOutAsyncSeq =
+ std::make_tuple(std::string("Pause"), kAidlVersion1, kStreamTransientStateTransitionDelayMs,
+ StreamTypeFilter::ASYNC, makePauseCommands(false, false),
+ false /*validatePositionIncrease*/);
std::shared_ptr<StateSequence> makeFlushCommands(bool isInput, bool isSync) {
using State = StreamDescriptor::State;
@@ -4780,14 +4834,15 @@
return std::make_shared<StateSequenceFollower>(std::move(d));
}
static const NamedCommandSequence kFlushInSeq =
- std::make_tuple(std::string("Flush"), 0, StreamTypeFilter::ANY,
+ std::make_tuple(std::string("Flush"), kAidlVersion1, 0, StreamTypeFilter::ANY,
makeFlushCommands(true, false), false /*validatePositionIncrease*/);
static const NamedCommandSequence kFlushOutSyncSeq =
- std::make_tuple(std::string("Flush"), 0, StreamTypeFilter::SYNC,
+ std::make_tuple(std::string("Flush"), kAidlVersion1, 0, StreamTypeFilter::SYNC,
makeFlushCommands(false, true), false /*validatePositionIncrease*/);
-static const NamedCommandSequence kFlushOutAsyncSeq = std::make_tuple(
- std::string("Flush"), kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
- makeFlushCommands(false, false), false /*validatePositionIncrease*/);
+static const NamedCommandSequence kFlushOutAsyncSeq =
+ std::make_tuple(std::string("Flush"), kAidlVersion1, kStreamTransientStateTransitionDelayMs,
+ StreamTypeFilter::ASYNC, makeFlushCommands(false, false),
+ false /*validatePositionIncrease*/);
std::shared_ptr<StateSequence> makeDrainPauseFlushOutCommands(bool isSync) {
using State = StreamDescriptor::State;
@@ -4807,13 +4862,13 @@
return std::make_shared<StateSequenceFollower>(std::move(d));
}
static const NamedCommandSequence kDrainPauseFlushOutSyncSeq =
- std::make_tuple(std::string("DrainPauseFlush"), kStreamTransientStateTransitionDelayMs,
- StreamTypeFilter::SYNC, makeDrainPauseFlushOutCommands(true),
- false /*validatePositionIncrease*/);
+ std::make_tuple(std::string("DrainPauseFlush"), kAidlVersion1,
+ kStreamTransientStateTransitionDelayMs, StreamTypeFilter::SYNC,
+ makeDrainPauseFlushOutCommands(true), false /*validatePositionIncrease*/);
static const NamedCommandSequence kDrainPauseFlushOutAsyncSeq =
- std::make_tuple(std::string("DrainPauseFlush"), kStreamTransientStateTransitionDelayMs,
- StreamTypeFilter::ASYNC, makeDrainPauseFlushOutCommands(false),
- false /*validatePositionIncrease*/);
+ std::make_tuple(std::string("DrainPauseFlush"), kAidlVersion1,
+ kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
+ makeDrainPauseFlushOutCommands(false), false /*validatePositionIncrease*/);
// Note, this isn't the "official" enum printer, it is only used to make the test name suffix.
std::string PrintStreamFilterToString(StreamTypeFilter filter) {
@@ -4851,9 +4906,10 @@
AudioStreamIoOutTest, AudioStreamIoOut,
testing::Combine(testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
testing::Values(kWriteSyncSeq, kWriteAsyncSeq, kWriteDrainAsyncSeq,
- kDrainOutSyncSeq, kDrainPauseOutSyncSeq,
- kDrainPauseOutAsyncSeq, kStandbyOutSyncSeq,
- kStandbyOutAsyncSeq,
+ kDrainOutSyncSeq, kDrainOutAsyncSeq,
+ kDrainEarlyOutAsyncSeq, kDrainPauseOutSyncSeq,
+ kDrainPauseOutAsyncSeq, kDrainEarlyPauseOutAsyncSeq,
+ kStandbyOutSyncSeq, kStandbyOutAsyncSeq,
kPauseOutSyncSeq, // kPauseOutAsyncSeq,
kFlushOutSyncSeq, kFlushOutAsyncSeq,
kDrainPauseFlushOutSyncSeq, kDrainPauseFlushOutAsyncSeq),