Merge "wifi vts: Add hostapd vts test case for mlo SAP" into main
diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
index ea59771..cef0ea6 100644
--- a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
+++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
@@ -77,17 +77,15 @@
}
::android::status_t StreamRemoteSubmix::drain(StreamDescriptor::DrainMode) {
- usleep(1000);
return ::android::OK;
}
::android::status_t StreamRemoteSubmix::flush() {
- usleep(1000);
- return ::android::OK;
+ // TODO(b/372951987): consider if this needs to be done from 'StreamInWorkerLogic::cycle'.
+ return mIsInput ? standby() : ::android::OK;
}
::android::status_t StreamRemoteSubmix::pause() {
- usleep(1000);
return ::android::OK;
}
diff --git a/audio/aidl/default/r_submix/SubmixRoute.cpp b/audio/aidl/default/r_submix/SubmixRoute.cpp
index 325a012..445b1d3 100644
--- a/audio/aidl/default/r_submix/SubmixRoute.cpp
+++ b/audio/aidl/default/r_submix/SubmixRoute.cpp
@@ -134,10 +134,10 @@
// - the peer input is in standby AFTER having been active.
// We DO block if:
// - the input was never activated to avoid discarding first frames in the pipe in case capture
-// start was delayed
+// start was delayed
bool SubmixRoute::shouldBlockWrite() {
std::lock_guard guard(mLock);
- return (mStreamInOpen || (mStreamInStandby && (mReadCounterFrames != 0)));
+ return mStreamInOpen && (!mStreamInStandby || mReadCounterFrames == 0);
}
long SubmixRoute::updateReadCounterFrames(size_t frameCount) {
diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
index 93c2a61..750e54d 100644
--- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
@@ -938,6 +938,8 @@
LOG(DEBUG) << __func__;
return "";
}
+ const std::vector<int8_t>& getData() const { return mData; }
+ void fillData(int8_t filler) { std::fill(mData.begin(), mData.end(), filler); }
std::optional<StreamDescriptor::Command> maybeGetNextCommand(int* actualSize = nullptr) {
TransitionTrigger trigger = mDriver->getNextTrigger(mData.size(), actualSize);
if (StreamEventReceiver::Event* expEvent =
@@ -1007,6 +1009,8 @@
StreamReaderLogic(const StreamContext& context, StreamLogicDriver* driver,
StreamEventReceiver* eventReceiver)
: StreamCommonLogic(context, driver, eventReceiver) {}
+ // Should only be called after the worker has joined.
+ const std::vector<int8_t>& getData() const { return StreamCommonLogic::getData(); }
protected:
Status cycle() override {
@@ -1072,6 +1076,7 @@
if (const size_t readCount =
!isMmapped() ? getDataMQ()->availableToRead() : reply.fmqByteCount;
readCount > 0) {
+ fillData(-1);
if (isMmapped() ? readDataFromMmap(readCount) : readDataFromMQ(readCount)) {
goto checkAcceptedReply;
}
@@ -1093,6 +1098,8 @@
StreamWriterLogic(const StreamContext& context, StreamLogicDriver* driver,
StreamEventReceiver* eventReceiver)
: StreamCommonLogic(context, driver, eventReceiver) {}
+ // Should only be called after the worker has joined.
+ const std::vector<int8_t>& getData() const { return StreamCommonLogic::getData(); }
protected:
Status cycle() override {
@@ -1109,6 +1116,14 @@
return Status::ABORT;
}
if (actualSize != 0) {
+ if (command.getTag() == StreamDescriptor::Command::burst) {
+ fillData(mBurstIteration);
+ if (mBurstIteration < std::numeric_limits<int8_t>::max()) {
+ mBurstIteration++;
+ } else {
+ mBurstIteration = 0;
+ }
+ }
if (isMmapped() ? !writeDataToMmap() : !writeDataToMQ()) {
return Status::ABORT;
}
@@ -1167,6 +1182,9 @@
LOG(ERROR) << __func__ << ": unacceptable reply: " << reply.toString();
return Status::ABORT;
}
+
+ private:
+ int8_t mBurstIteration = 1;
};
using StreamWriter = StreamWorker<StreamWriterLogic>;
@@ -2859,10 +2877,12 @@
ASSERT_NO_FATAL_FAILURE(mStream->SetUpStream(module, getMinimumStreamBufferSizeFrames()));
}
- void SetUpStreamForDevicePort(IModule* module, ModuleConfig* moduleConfig,
- const AudioPort& devicePort, bool connectedOnly = false) {
- ASSERT_NO_FATAL_FAILURE(
- SetUpPortConfigForDevicePort(module, moduleConfig, devicePort, connectedOnly));
+ void SetUpStreamForDevicePort(
+ IModule* module, ModuleConfig* moduleConfig, const AudioPort& devicePort,
+ bool connectedOnly = false,
+ const std::optional<AudioDeviceAddress>& connectionAddress = std::nullopt) {
+ ASSERT_NO_FATAL_FAILURE(SetUpPortConfigForDevicePort(module, moduleConfig, devicePort,
+ connectedOnly, connectionAddress));
if (!mSkipTestReason.empty()) return;
ASSERT_NO_FATAL_FAILURE(SetUpStream(module));
}
@@ -2898,6 +2918,23 @@
if (!mSkipTestReason.empty()) return;
ASSERT_NO_FATAL_FAILURE(SetUpStream(module));
}
+ void SetUpStreamForNewMixPortConfig(IModule* module, ModuleConfig*,
+ const AudioPortConfig& existingMixPortConfig,
+ const AudioPortConfig& existingDevicePortConfig) {
+ auto mixPortConfig = existingMixPortConfig;
+ mixPortConfig.id = 0;
+ mMixPortConfig = std::make_unique<WithAudioPortConfig>(mixPortConfig);
+ ASSERT_NO_FATAL_FAILURE(mMixPortConfig->SetUp(module));
+ mDevicePortConfig = std::make_unique<WithAudioPortConfig>(existingDevicePortConfig);
+ ASSERT_NO_FATAL_FAILURE(mDevicePortConfig->SetUp(module));
+ mDevice = existingDevicePortConfig.ext.get<AudioPortExt::device>().device;
+ mPatch = std::make_unique<WithAudioPatch>(mIsInput, mMixPortConfig->get(),
+ mDevicePortConfig->get());
+ ASSERT_NO_FATAL_FAILURE(mPatch->SetUp(module));
+ mStream = std::make_unique<WithStream<Stream>>(mMixPortConfig->get());
+ ASSERT_NO_FATAL_FAILURE(mStream->SetUpPortConfig(module));
+ ASSERT_NO_FATAL_FAILURE(SetUpStream(module));
+ }
void SetUpPatchForMixPortConfig(IModule* module, ModuleConfig* moduleConfig,
const AudioPortConfig& mixPortConfig) {
constexpr bool connectedOnly = true;
@@ -2929,6 +2966,7 @@
}
const AudioDevice& getDevice() const { return mDevice; }
+ const AudioPortConfig& getDevicePortConfig() const { return mDevicePortConfig->get(); }
int32_t getMinimumStreamBufferSizeFrames() const {
return mPatch->getMinimumStreamBufferSizeFrames();
}
@@ -2944,7 +2982,8 @@
private:
void SetUpDevicePort(IModule* module, ModuleConfig* moduleConfig,
const std::set<int32_t>& devicePortIds, bool connectedOnly,
- std::optional<AudioPort>* connectedDevicePort) {
+ std::optional<AudioPort>* connectedDevicePort,
+ const std::optional<AudioDeviceAddress>& connectionAddress) {
const auto attachedDevicePorts = moduleConfig->getAttachedDevicePorts();
if (auto it = findAny<AudioPort>(attachedDevicePorts, devicePortIds);
it != attachedDevicePorts.end()) {
@@ -2961,7 +3000,12 @@
const auto externalDevicePorts = moduleConfig->getExternalDevicePorts();
if (auto it = findAny<AudioPort>(externalDevicePorts, devicePortIds);
it != externalDevicePorts.end()) {
- AudioPort portWithData = GenerateUniqueDeviceAddress(*it);
+ AudioPort portWithData = *it;
+ if (connectionAddress.has_value()) {
+ portWithData.ext.get<AudioPortExt::Tag::device>().device.address =
+ *connectionAddress;
+ }
+ portWithData = GenerateUniqueDeviceAddress(portWithData);
mPortConnected = std::make_unique<WithDevicePortConnectedState>(portWithData);
ASSERT_NO_FATAL_FAILURE(mPortConnected->SetUp(module, moduleConfig));
*connectedDevicePort = mPortConnected->get();
@@ -2980,9 +3024,9 @@
LOG(DEBUG) << __func__ << ": " << mSkipTestReason;
return;
};
- ASSERT_NO_FATAL_FAILURE(SetUpDevicePort(module, moduleConfig,
- extractIds<AudioPort>(devicePorts), connectedOnly,
- connectedDevicePort));
+ ASSERT_NO_FATAL_FAILURE(SetUpDevicePort(
+ module, moduleConfig, extractIds<AudioPort>(devicePorts), connectedOnly,
+ connectedDevicePort, std::nullopt /*connectionAddress*/));
if (!connectedDevicePort->has_value()) {
mSkipTestReason = std::string("Unable to find a device port pair for mix port id ")
.append(std::to_string(mixPort.id));
@@ -2990,11 +3034,14 @@
return;
}
}
- void SetUpPortConfigForDevicePort(IModule* module, ModuleConfig* moduleConfig,
- const AudioPort& devicePort, bool connectedOnly) {
+ void SetUpPortConfigForDevicePort(
+ IModule* module, ModuleConfig* moduleConfig, const AudioPort& devicePort,
+ bool connectedOnly,
+ const std::optional<AudioDeviceAddress>& connectionAddress = std::nullopt) {
std::optional<AudioPort> connectedDevicePort;
ASSERT_NO_FATAL_FAILURE(SetUpDevicePort(module, moduleConfig, {devicePort.id},
- connectedOnly, &connectedDevicePort));
+ connectedOnly, &connectedDevicePort,
+ connectionAddress));
if (!connectedDevicePort.has_value()) {
mSkipTestReason = std::string("Device port id ")
.append(std::to_string(devicePort.id))
@@ -3135,7 +3182,8 @@
};
// Defined later together with state transition sequences.
-std::shared_ptr<StateSequence> makeBurstCommands(bool isSync);
+std::shared_ptr<StateSequence> makeBurstCommands(bool isSync, size_t burstCount = 10,
+ bool standbyInputWhenDone = false);
// Certain types of ports can not be used without special preconditions.
static bool skipStreamIoTestForMixPortConfig(const AudioPortConfig& portConfig) {
@@ -3160,10 +3208,11 @@
public:
explicit StreamFixtureWithWorker(bool isSync) : mIsSync(isSync) {}
- void SetUp(IModule* module, ModuleConfig* moduleConfig, const AudioPort& devicePort) {
+ void SetUp(IModule* module, ModuleConfig* moduleConfig, const AudioPort& devicePort,
+ const std::optional<AudioDeviceAddress>& connectionAddress = std::nullopt) {
mStream = std::make_unique<StreamFixture<Stream>>();
- ASSERT_NO_FATAL_FAILURE(
- mStream->SetUpStreamForDevicePort(module, moduleConfig, devicePort));
+ ASSERT_NO_FATAL_FAILURE(mStream->SetUpStreamForDevicePort(
+ module, moduleConfig, devicePort, false /*connectedOnly*/, connectionAddress));
MaybeSetSkipTestReason();
}
@@ -3175,26 +3224,42 @@
MaybeSetSkipTestReason();
}
- void SendBurstCommands(bool validatePosition = true) {
- ASSERT_NO_FATAL_FAILURE(StartWorkerToSendBurstCommands());
+ void SetUp(IModule* module, ModuleConfig* moduleConfig,
+ const AudioPortConfig& existingMixPortConfig,
+ const AudioPortConfig& existingDevicePortConfig) {
+ mStream = std::make_unique<StreamFixture<Stream>>();
+ ASSERT_NO_FATAL_FAILURE(mStream->SetUpStreamForNewMixPortConfig(
+ module, moduleConfig, existingMixPortConfig, existingDevicePortConfig));
+ MaybeSetSkipTestReason();
+ }
+
+ void SendBurstCommands(bool validatePosition = true, size_t burstCount = 10,
+ bool standbyInputWhenDone = false) {
+ ASSERT_NO_FATAL_FAILURE(StartWorkerToSendBurstCommands(burstCount, standbyInputWhenDone));
ASSERT_NO_FATAL_FAILURE(JoinWorkerAfterBurstCommands(validatePosition));
}
- void StartWorkerToSendBurstCommands() {
+ void StartWorkerToSendBurstCommands(size_t burstCount = 10, bool standbyInputWhenDone = false) {
+ if (!IOTraits<Stream>::is_input) {
+ ASSERT_FALSE(standbyInputWhenDone) << "Only supported for input";
+ }
const StreamContext* context = mStream->getStreamContext();
mWorkerDriver = std::make_unique<StreamLogicDefaultDriver>(
- makeBurstCommands(mIsSync), context->getFrameSizeBytes(), context->isMmapped());
+ makeBurstCommands(mIsSync, burstCount, standbyInputWhenDone),
+ context->getFrameSizeBytes(), context->isMmapped());
mWorker = std::make_unique<typename IOTraits<Stream>::Worker>(
*context, mWorkerDriver.get(), mStream->getStreamEventReceiver());
LOG(DEBUG) << __func__ << ": starting " << IOTraits<Stream>::directionStr << " worker...";
ASSERT_TRUE(mWorker->start());
}
- void JoinWorkerAfterBurstCommands(bool validatePosition = true) {
- // Must call 'prepareToClose' before attempting to join because the stream may be stuck.
- std::shared_ptr<IStreamCommon> common;
- ASSERT_IS_OK(mStream->getStream()->getStreamCommon(&common));
- ASSERT_IS_OK(common->prepareToClose());
+ void JoinWorkerAfterBurstCommands(bool validatePosition = true,
+ bool callPrepareToClose = true) {
+ if (callPrepareToClose) {
+ std::shared_ptr<IStreamCommon> common;
+ ASSERT_IS_OK(mStream->getStream()->getStreamCommon(&common));
+ ASSERT_IS_OK(common->prepareToClose());
+ }
LOG(DEBUG) << __func__ << ": joining " << IOTraits<Stream>::directionStr << " worker...";
mWorker->join();
EXPECT_FALSE(mWorker->hasError()) << mWorker->getError();
@@ -3205,6 +3270,7 @@
EXPECT_FALSE(mWorkerDriver->hasObservableRetrogradePosition());
EXPECT_FALSE(mWorkerDriver->hasHardwareRetrogradePosition());
}
+ mLastData = mWorker->getData();
mWorker.reset();
mWorkerDriver.reset();
}
@@ -3212,6 +3278,9 @@
void TeardownPatch() { mStream->TeardownPatch(); }
const AudioDevice& getDevice() const { return mStream->getDevice(); }
+ const AudioPortConfig& getDevicePortConfig() const { return mStream->getDevicePortConfig(); }
+ const std::vector<int8_t>& getLastData() const { return mLastData; }
+ const AudioPortConfig& getPortConfig() const { return mStream->getPortConfig(); }
Stream* getStream() const { return mStream->getStream(); }
std::string skipTestReason() const {
return !mSkipTestReason.empty() ? mSkipTestReason : mStream->skipTestReason();
@@ -3229,6 +3298,7 @@
std::unique_ptr<StreamFixture<Stream>> mStream;
std::unique_ptr<StreamLogicDefaultDriver> mWorkerDriver;
std::unique_ptr<typename IOTraits<Stream>::Worker> mWorker;
+ std::vector<int8_t> mLastData;
};
template <typename Stream>
@@ -4576,15 +4646,20 @@
// TODO: Add async test cases for input once it is implemented.
-std::shared_ptr<StateSequence> makeBurstCommands(bool isSync) {
+std::shared_ptr<StateSequence> makeBurstCommands(bool isSync, size_t burstCount,
+ bool standbyInputWhenDone) {
using State = StreamDescriptor::State;
auto d = std::make_unique<StateDag>();
- StateDag::Node last = d->makeFinalNode(State::ACTIVE);
+ StateDag::Node active = d->makeFinalNode(State::ACTIVE);
+ StateDag::Node paused = d->makeNodes({std::make_pair(State::ACTIVE, kPauseCommand),
+ std::make_pair(State::PAUSED, kFlushCommand)},
+ State::STANDBY);
+ StateDag::Node& last = standbyInputWhenDone ? paused : active;
if (isSync) {
StateDag::Node idle = d->makeNode(
State::IDLE, kBurstCommand,
// Use several bursts to ensure that the driver starts reporting the position.
- d->makeNodes(State::ACTIVE, kBurstCommand, 10, last));
+ d->makeNodes(State::ACTIVE, kBurstCommand, burstCount, last));
d->makeNode(State::STANDBY, kStartCommand, idle);
} else {
StateDag::Node active2 = d->makeNode(State::ACTIVE, kBurstCommand, last);
@@ -4949,49 +5024,69 @@
public:
WithRemoteSubmix() : mStream(true /*isSync*/) {}
explicit WithRemoteSubmix(AudioDeviceAddress address)
- : mStream(true /*isSync*/), mAddress(address) {}
+ : mStream(true /*isSync*/), mAddress(address) {
+ LOG(DEBUG) << __func__ << ": Creating " << IOTraits<Stream>::directionStr
+ << " stream for: " << mAddress.value_or(AudioDeviceAddress{}).toString();
+ }
WithRemoteSubmix(const WithRemoteSubmix&) = delete;
WithRemoteSubmix& operator=(const WithRemoteSubmix&) = delete;
+ ~WithRemoteSubmix() {
+ LOG(DEBUG) << __func__ << ": Deleting " << IOTraits<Stream>::directionStr
+ << " stream for: " << mAddress.value_or(AudioDeviceAddress{}).toString();
+ }
- static std::optional<AudioPort> getRemoteSubmixAudioPort(
- ModuleConfig* moduleConfig,
- const std::optional<AudioDeviceAddress>& address = std::nullopt) {
+ static std::optional<AudioPort> getRemoteSubmixAudioPort(ModuleConfig* moduleConfig) {
auto ports =
moduleConfig->getRemoteSubmixPorts(IOTraits<Stream>::is_input, true /*singlePort*/);
if (ports.empty()) return {};
- AudioPort port = ports.front();
- if (address) {
- port.ext.template get<AudioPortExt::Tag::device>().device.address = address.value();
- }
- return port;
+ return ports.front();
}
void SetUp(IModule* module, ModuleConfig* moduleConfig) {
- auto devicePort = getRemoteSubmixAudioPort(moduleConfig, mAddress);
+ auto devicePort = getRemoteSubmixAudioPort(moduleConfig);
ASSERT_TRUE(devicePort.has_value()) << "Device port for remote submix device not found";
- ASSERT_NO_FATAL_FAILURE(mStream.SetUp(module, moduleConfig, *devicePort));
+ ASSERT_NO_FATAL_FAILURE(mStream.SetUp(module, moduleConfig, *devicePort, mAddress));
mAddress = mStream.getDevice().address;
}
-
- void StartWorkerToSendBurstCommands() {
- ASSERT_NO_FATAL_FAILURE(mStream.StartWorkerToSendBurstCommands());
+ void SetUp(IModule* module, ModuleConfig* moduleConfig,
+ const AudioPortConfig& existingMixPortConfig,
+ const AudioPortConfig& existingDevicePortConfig) {
+ ASSERT_NO_FATAL_FAILURE(mStream.SetUp(module, moduleConfig, existingMixPortConfig,
+ existingDevicePortConfig));
+ mAddress = mStream.getDevice().address;
+ }
+ void StartWorkerToSendBurstCommands(size_t burstCount = 10, bool standbyInputWhenDone = false) {
+ ASSERT_NO_FATAL_FAILURE(
+ mStream.StartWorkerToSendBurstCommands(burstCount, standbyInputWhenDone));
}
- void JoinWorkerAfterBurstCommands() {
- ASSERT_NO_FATAL_FAILURE(mStream.JoinWorkerAfterBurstCommands());
+ void JoinWorkerAfterBurstCommands(bool callPrepareToCloseBeforeJoin) {
+ ASSERT_NO_FATAL_FAILURE(mStream.JoinWorkerAfterBurstCommands(
+ true /*validatePositionIncrease*/, callPrepareToCloseBeforeJoin));
}
- void SendBurstCommands() {
- ASSERT_NO_FATAL_FAILURE(mStream.StartWorkerToSendBurstCommands());
- ASSERT_NO_FATAL_FAILURE(mStream.JoinWorkerAfterBurstCommands());
+ void JoinWorkerAfterBurstCommands(bool validatePositionIncrease,
+ bool callPrepareToCloseBeforeJoin) {
+ ASSERT_NO_FATAL_FAILURE(mStream.JoinWorkerAfterBurstCommands(validatePositionIncrease,
+ callPrepareToCloseBeforeJoin));
+ }
+
+ void SendBurstCommands(bool callPrepareToCloseBeforeJoin, size_t burstCount = 10,
+ bool standbyInputWhenDone = false) {
+ ASSERT_NO_FATAL_FAILURE(StartWorkerToSendBurstCommands(burstCount, standbyInputWhenDone));
+ // When 'burstCount == 0', there is no "previous" frame count, thus the check for
+ // the position increase fails.
+ ASSERT_NO_FATAL_FAILURE(JoinWorkerAfterBurstCommands(
+ burstCount > 0 /*validatePositionIncrease*/, callPrepareToCloseBeforeJoin));
}
std::optional<AudioDeviceAddress> getAudioDeviceAddress() const { return mAddress; }
+ const AudioPortConfig& getDevicePortConfig() const { return mStream.getDevicePortConfig(); }
+ int8_t getLastBurstIteration() const { return mStream.getLastData()[0]; }
+ const AudioPortConfig& getPortConfig() const { return mStream.getPortConfig(); }
std::string skipTestReason() const { return mStream.skipTestReason(); }
private:
- void SetUp(IModule* module, ModuleConfig* moduleConfig, const AudioPort& devicePort) {}
-
StreamFixtureWithWorker<Stream> mStream;
std::optional<AudioDeviceAddress> mAddress;
};
@@ -5007,77 +5102,130 @@
}
ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
}
+
+ void TearDown() override {
+ streamIn.reset();
+ streamOut.reset();
+ }
+
+ void CreateOutputStream() {
+ streamOut = std::make_unique<WithRemoteSubmix<IStreamOut>>();
+ ASSERT_NO_FATAL_FAILURE(streamOut->SetUp(module.get(), moduleConfig.get()));
+ // Note: any issue with connection attempts is considered as a problem.
+ ASSERT_EQ("", streamOut->skipTestReason());
+ ASSERT_TRUE(streamOut->getAudioDeviceAddress().has_value());
+ }
+
+ void CreateInputStream(const std::optional<AudioDeviceAddress>& address = std::nullopt) {
+ if (address.has_value()) {
+ streamIn = std::make_unique<WithRemoteSubmix<IStreamIn>>(address.value());
+ } else {
+ ASSERT_TRUE(streamOut->getAudioDeviceAddress().has_value());
+ streamIn = std::make_unique<WithRemoteSubmix<IStreamIn>>(
+ streamOut->getAudioDeviceAddress().value());
+ }
+ ASSERT_NO_FATAL_FAILURE(streamIn->SetUp(module.get(), moduleConfig.get()));
+ ASSERT_EQ("", streamIn->skipTestReason());
+ auto inAddress = streamIn->getAudioDeviceAddress();
+ ASSERT_TRUE(inAddress.has_value());
+ if (address.has_value()) {
+ if (address.value() != AudioDeviceAddress{}) {
+ ASSERT_EQ(address.value(), inAddress.value());
+ }
+ } else {
+ ASSERT_EQ(streamOut->getAudioDeviceAddress().value(), inAddress.value());
+ }
+ }
+
+ std::unique_ptr<WithRemoteSubmix<IStreamOut>> streamOut;
+ std::unique_ptr<WithRemoteSubmix<IStreamIn>> streamIn;
};
TEST_P(AudioModuleRemoteSubmix, OutputDoesNotBlockWhenNoInput) {
- WithRemoteSubmix<IStreamOut> streamOut;
- ASSERT_NO_FATAL_FAILURE(streamOut.SetUp(module.get(), moduleConfig.get()));
- // Note: here and in other tests any issue with connection attempts is considered as a problem.
- ASSERT_EQ("", streamOut.skipTestReason());
- ASSERT_NO_FATAL_FAILURE(streamOut.SendBurstCommands());
+ ASSERT_NO_FATAL_FAILURE(CreateOutputStream());
+ ASSERT_NO_FATAL_FAILURE(streamOut->SendBurstCommands(false /*callPrepareToCloseBeforeJoin*/));
}
-TEST_P(AudioModuleRemoteSubmix, OutputDoesNotBlockWhenInputStuck) {
- WithRemoteSubmix<IStreamOut> streamOut;
- ASSERT_NO_FATAL_FAILURE(streamOut.SetUp(module.get(), moduleConfig.get()));
- ASSERT_EQ("", streamOut.skipTestReason());
- auto address = streamOut.getAudioDeviceAddress();
- ASSERT_TRUE(address.has_value());
+TEST_P(AudioModuleRemoteSubmix, OutputDoesNotBlockWhenInputInStandby) {
+ if (int32_t version; module->getInterfaceVersion(&version).isOk() && version < 3) {
+ GTEST_SKIP() << "Default remote submix implementation <V3 could not pass this test";
+ }
+ ASSERT_NO_FATAL_FAILURE(CreateOutputStream());
+ ASSERT_NO_FATAL_FAILURE(CreateInputStream());
+ ASSERT_NO_FATAL_FAILURE(streamOut->StartWorkerToSendBurstCommands());
+ // Send just 1 burst command. This triggers the condition "input is in standby after
+ // being active." The output must flush the fifo before writing to avoid being blocked.
+ ASSERT_NO_FATAL_FAILURE(
+ streamIn->StartWorkerToSendBurstCommands(1, true /*stanbyInputWhenDone*/));
+ // The output must be able to close without shutting down the pipe first (due to a call
+ // to 'prepareToClose').
+ ASSERT_NO_FATAL_FAILURE(
+ streamOut->JoinWorkerAfterBurstCommands(false /*callPrepareToCloseBeforeJoin*/));
+ ASSERT_NO_FATAL_FAILURE(
+ streamIn->JoinWorkerAfterBurstCommands(false /*callPrepareToCloseBeforeJoin*/));
+}
- WithRemoteSubmix<IStreamIn> streamIn(address.value());
- ASSERT_NO_FATAL_FAILURE(streamIn.SetUp(module.get(), moduleConfig.get()));
- ASSERT_EQ("", streamIn.skipTestReason());
+TEST_P(AudioModuleRemoteSubmix, BlockedOutputUnblocksOnClose) {
+ ASSERT_NO_FATAL_FAILURE(CreateOutputStream());
+ ASSERT_NO_FATAL_FAILURE(CreateInputStream());
+ ASSERT_NO_FATAL_FAILURE(streamOut->StartWorkerToSendBurstCommands());
+ // Send just 3 burst command, but do not enter standby. This is a stalled input.
+ ASSERT_NO_FATAL_FAILURE(streamIn->StartWorkerToSendBurstCommands(3));
+ ASSERT_NO_FATAL_FAILURE(
+ streamOut->JoinWorkerAfterBurstCommands(true /*callPrepareToCloseBeforeJoin*/));
+ ASSERT_NO_FATAL_FAILURE(
+ streamIn->JoinWorkerAfterBurstCommands(false /*callPrepareToCloseBeforeJoin*/));
+}
- ASSERT_NO_FATAL_FAILURE(streamOut.SendBurstCommands());
+TEST_P(AudioModuleRemoteSubmix, OutputBlocksUntilInputStarts) {
+ ASSERT_NO_FATAL_FAILURE(CreateOutputStream());
+ ASSERT_NO_FATAL_FAILURE(CreateInputStream());
+ ASSERT_NO_FATAL_FAILURE(streamOut->StartWorkerToSendBurstCommands());
+ // Read the head of the pipe and check that it starts with the first output burst, that is,
+ // the contents of the very first write has not been superseded due to pipe overflow.
+ // The burstCount is '0' because the very first burst is used to exit from the 'IDLE' state,
+ // see 'makeBurstCommands'.
+ ASSERT_NO_FATAL_FAILURE(streamIn->SendBurstCommands(false /*callPrepareToCloseBeforeJoin*/, 0,
+ true /*standbyInputWhenDone*/));
+ EXPECT_EQ(1, streamIn->getLastBurstIteration());
+ ASSERT_NO_FATAL_FAILURE(
+ streamOut->JoinWorkerAfterBurstCommands(true /*callPrepareToCloseBeforeJoin*/));
}
TEST_P(AudioModuleRemoteSubmix, OutputAndInput) {
- WithRemoteSubmix<IStreamOut> streamOut;
- ASSERT_NO_FATAL_FAILURE(streamOut.SetUp(module.get(), moduleConfig.get()));
- ASSERT_EQ("", streamOut.skipTestReason());
- auto address = streamOut.getAudioDeviceAddress();
- ASSERT_TRUE(address.has_value());
-
- WithRemoteSubmix<IStreamIn> streamIn(address.value());
- ASSERT_NO_FATAL_FAILURE(streamIn.SetUp(module.get(), moduleConfig.get()));
- ASSERT_EQ("", streamIn.skipTestReason());
-
+ ASSERT_NO_FATAL_FAILURE(CreateOutputStream());
+ ASSERT_NO_FATAL_FAILURE(CreateInputStream());
// Start writing into the output stream.
- ASSERT_NO_FATAL_FAILURE(streamOut.StartWorkerToSendBurstCommands());
+ ASSERT_NO_FATAL_FAILURE(streamOut->StartWorkerToSendBurstCommands());
// Simultaneously, read from the input stream.
- ASSERT_NO_FATAL_FAILURE(streamIn.SendBurstCommands());
- ASSERT_NO_FATAL_FAILURE(streamOut.JoinWorkerAfterBurstCommands());
+ ASSERT_NO_FATAL_FAILURE(streamIn->SendBurstCommands(false /*callPrepareToCloseBeforeJoin*/));
+ ASSERT_NO_FATAL_FAILURE(
+ streamOut->JoinWorkerAfterBurstCommands(false /*callPrepareToCloseBeforeJoin*/));
}
TEST_P(AudioModuleRemoteSubmix, OpenInputMultipleTimes) {
- WithRemoteSubmix<IStreamOut> streamOut;
- ASSERT_NO_FATAL_FAILURE(streamOut.SetUp(module.get(), moduleConfig.get()));
- ASSERT_EQ("", streamOut.skipTestReason());
- auto address = streamOut.getAudioDeviceAddress();
- ASSERT_TRUE(address.has_value());
-
- const size_t streamInCount = 3;
- std::vector<std::unique_ptr<WithRemoteSubmix<IStreamIn>>> streamIns(streamInCount);
- for (size_t i = 0; i < streamInCount; i++) {
- streamIns[i] = std::make_unique<WithRemoteSubmix<IStreamIn>>(address.value());
- ASSERT_NO_FATAL_FAILURE(streamIns[i]->SetUp(module.get(), moduleConfig.get()));
+ ASSERT_NO_FATAL_FAILURE(CreateOutputStream());
+ ASSERT_NO_FATAL_FAILURE(CreateInputStream());
+ ASSERT_NO_FATAL_FAILURE(streamOut->StartWorkerToSendBurstCommands());
+ ASSERT_NO_FATAL_FAILURE(streamIn->SendBurstCommands(false /*callPrepareToCloseBeforeJoin*/, 1,
+ true /*standbyInputWhenDone*/));
+ // For the new stream, only create a new mix port config and a new patch.
+ const size_t extraStreamInCount = 2;
+ std::vector<std::unique_ptr<WithRemoteSubmix<IStreamIn>>> streamIns(extraStreamInCount);
+ for (size_t i = 0; i < extraStreamInCount; i++) {
+ streamIns[i] = std::make_unique<WithRemoteSubmix<IStreamIn>>();
+ ASSERT_NO_FATAL_FAILURE(streamIns[i]->SetUp(module.get(), moduleConfig.get(),
+ streamIn->getPortConfig(),
+ streamIn->getDevicePortConfig()));
ASSERT_EQ("", streamIns[i]->skipTestReason());
+ const auto inAddress = streamIns[i]->getAudioDeviceAddress();
+ ASSERT_TRUE(inAddress.has_value());
+ ASSERT_EQ(streamOut->getAudioDeviceAddress().value(), inAddress.value());
+ ASSERT_NO_FATAL_FAILURE(streamIns[i]->SendBurstCommands(
+ false /*callPrepareToCloseBeforeJoin*/, 1, true /*standbyInputWhenDone*/));
}
- // Start writing into the output stream.
- ASSERT_NO_FATAL_FAILURE(streamOut.StartWorkerToSendBurstCommands());
- // Simultaneously, read from input streams.
- for (size_t i = 0; i < streamInCount; i++) {
- ASSERT_NO_FATAL_FAILURE(streamIns[i]->StartWorkerToSendBurstCommands());
- }
- for (size_t i = 0; i < streamInCount; i++) {
- ASSERT_NO_FATAL_FAILURE(streamIns[i]->JoinWorkerAfterBurstCommands());
- }
- ASSERT_NO_FATAL_FAILURE(streamOut.JoinWorkerAfterBurstCommands());
- // Clean up input streams in the reverse order because the device connection is owned
- // by the first one.
- for (size_t i = streamInCount; i != 0; --i) {
- streamIns[i - 1].reset();
- }
+ ASSERT_NO_FATAL_FAILURE(
+ streamOut->JoinWorkerAfterBurstCommands(false /*callPrepareToCloseBeforeJoin*/));
}
INSTANTIATE_TEST_SUITE_P(AudioModuleRemoteSubmixTest, AudioModuleRemoteSubmix,
diff --git a/automotive/vehicle/aidl/impl/current/hardware/include/IVehicleHardware.h b/automotive/vehicle/aidl/impl/current/hardware/include/IVehicleHardware.h
index f46a1c8..9122955 100644
--- a/automotive/vehicle/aidl/impl/current/hardware/include/IVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/current/hardware/include/IVehicleHardware.h
@@ -303,6 +303,8 @@
//
// If the propertyId's supported values are static, then must do nothing.
//
+ // If some of the [propId, areaId]s are already subscribed, then do nothing.
+ //
// This is only called for [propId, areaId] that has non-null {@code HasSupportedValueInfo}.
//
// Client must implement (override) this function if at least one [propId, areaId]'s
diff --git a/automotive/vehicle/aidl/impl/current/vhal/include/DefaultVehicleHal.h b/automotive/vehicle/aidl/impl/current/vhal/include/DefaultVehicleHal.h
index 8785bcd..02dbe9e 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/include/DefaultVehicleHal.h
+++ b/automotive/vehicle/aidl/impl/current/vhal/include/DefaultVehicleHal.h
@@ -233,9 +233,9 @@
std::function<void(const std::unordered_map<int32_t, aidlvhal::VehiclePropConfig>&)>
callback) const EXCLUDES(mConfigLock);
- android::base::Result<const aidlvhal::VehicleAreaConfig*> getAreaConfigForPropIdAreaId(
+ android::base::Result<aidlvhal::VehicleAreaConfig> getAreaConfigForPropIdAreaId(
int32_t propId, int32_t areaId) const;
- android::base::Result<const aidlvhal::HasSupportedValueInfo*> getHasSupportedValueInfo(
+ android::base::Result<aidlvhal::HasSupportedValueInfo> getHasSupportedValueInfo(
int32_t propId, int32_t areaId) const;
// Puts the property change events into a queue so that they can handled in batch.
static void batchPropertyChangeEvent(
diff --git a/automotive/vehicle/aidl/impl/current/vhal/include/SubscriptionManager.h b/automotive/vehicle/aidl/impl/current/vhal/include/SubscriptionManager.h
index cac1901..59c21aa 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/include/SubscriptionManager.h
+++ b/automotive/vehicle/aidl/impl/current/vhal/include/SubscriptionManager.h
@@ -118,8 +118,19 @@
std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropError>>
getSubscribedClientsForErrorEvents(const std::vector<SetValueErrorEvent>& errorEvents);
- // Returns the number of subscribed clients.
- size_t countClients();
+ // Subscribes to supported values change.
+ VhalResult<void> subscribeSupportedValueChange(const CallbackType& callback,
+ const std::vector<PropIdAreaId>& propIdAreaIds);
+
+ // Unsubscribes to supported values change.
+ VhalResult<void> unsubscribeSupportedValueChange(
+ ClientIdType client, const std::vector<PropIdAreaId>& propIdAreaIds);
+
+ // Returns the number of subscribed property change clients.
+ size_t countPropertyChangeClients();
+
+ // Returns the number of subscribed supported value change clients.
+ size_t countSupportedValueChangeClients();
// Checks whether the sample rate is valid.
static bool checkSampleRateHz(float sampleRateHz);
@@ -160,6 +171,11 @@
std::unordered_set<VehiclePropValue, VehiclePropValueHashPropIdAreaId,
VehiclePropValueEqualPropIdAreaId>>
mContSubValuesByCallback GUARDED_BY(mLock);
+ std::unordered_map<PropIdAreaId, std::unordered_map<ClientIdType, CallbackType>,
+ PropIdAreaIdHash>
+ mSupportedValueChangeClientsByPropIdAreaId GUARDED_BY(mLock);
+ std::unordered_map<ClientIdType, std::unordered_set<PropIdAreaId, PropIdAreaIdHash>>
+ mSupportedValueChangePropIdAreaIdsByClient GUARDED_BY(mLock);
VhalResult<void> addContinuousSubscriberLocked(const ClientIdType& clientId,
const PropIdAreaId& propIdAreaId,
@@ -180,6 +196,9 @@
VhalResult<void> unsubscribePropIdAreaIdLocked(SubscriptionManager::ClientIdType clientId,
const PropIdAreaId& propIdAreaId)
REQUIRES(mLock);
+ VhalResult<void> unsubscribeSupportedValueChangeLocked(
+ SubscriptionManager::ClientIdType clientId,
+ const std::vector<PropIdAreaId>& propIdAreaIds) REQUIRES(mLock);
// Checks whether the manager is empty. For testing purpose.
bool isEmpty();
diff --git a/automotive/vehicle/aidl/impl/current/vhal/src/DefaultVehicleHal.cpp b/automotive/vehicle/aidl/impl/current/vhal/src/DefaultVehicleHal.cpp
index 509d1c3..25af50e 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/src/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/aidl/impl/current/vhal/src/DefaultVehicleHal.cpp
@@ -82,9 +82,6 @@
using VhalPropIdAreaId = ::aidl::android::hardware::automotive::vehicle::PropIdAreaId;
-#define propIdtoString(PROP_ID) \
- aidl::android::hardware::automotive::vehicle::toString(static_cast<VehicleProperty>(PROP_ID))
-
std::string toString(const std::unordered_set<int64_t>& values) {
std::string str = "";
for (auto it = values.begin(); it != values.end(); it++) {
@@ -972,25 +969,25 @@
return ScopedAStatus::ok();
}
-Result<const VehicleAreaConfig*> DefaultVehicleHal::getAreaConfigForPropIdAreaId(
- int32_t propId, int32_t areaId) const {
+Result<VehicleAreaConfig> DefaultVehicleHal::getAreaConfigForPropIdAreaId(int32_t propId,
+ int32_t areaId) const {
auto result = getConfig(propId);
if (!result.ok()) {
- return Error() << "Failed to get property config for propertyId: " << propIdtoString(propId)
+ return Error() << "Failed to get property config for propertyId: " << propIdToString(propId)
<< ", error: " << result.error();
}
const VehiclePropConfig& config = result.value();
const VehicleAreaConfig* areaConfig = getAreaConfig(propId, areaId, config);
if (areaConfig == nullptr) {
- return Error() << "AreaId config not found for propertyId: " << propIdtoString(propId)
+ return Error() << "AreaId config not found for propertyId: " << propIdToString(propId)
<< ", areaId: " << areaId;
}
- return areaConfig;
+ return *areaConfig;
}
-Result<const HasSupportedValueInfo*> DefaultVehicleHal::getHasSupportedValueInfo(
- int32_t propId, int32_t areaId) const {
- Result<const VehicleAreaConfig*> propIdAreaIdConfigResult =
+Result<HasSupportedValueInfo> DefaultVehicleHal::getHasSupportedValueInfo(int32_t propId,
+ int32_t areaId) const {
+ Result<VehicleAreaConfig> propIdAreaIdConfigResult =
getAreaConfigForPropIdAreaId(propId, areaId);
if (!isGlobalProp(propId) && !propIdAreaIdConfigResult.ok()) {
// For global property, it is possible that no config exists.
@@ -998,11 +995,11 @@
}
if (propIdAreaIdConfigResult.has_value()) {
auto areaConfig = propIdAreaIdConfigResult.value();
- if (areaConfig->hasSupportedValueInfo.has_value()) {
- return &(areaConfig->hasSupportedValueInfo.value());
+ if (areaConfig.hasSupportedValueInfo.has_value()) {
+ return areaConfig.hasSupportedValueInfo.value();
}
}
- return Error() << "property: " << propIdtoString(propId) << ", areaId: " << areaId
+ return Error() << "property: " << propIdToString(propId) << ", areaId: " << areaId
<< " does not support this operation because hasSupportedValueInfo is null";
}
@@ -1026,7 +1023,7 @@
continue;
}
- const auto& hasSupportedValueInfo = *(hasSupportedValueInfoResult.value());
+ const auto& hasSupportedValueInfo = hasSupportedValueInfoResult.value();
if (hasSupportedValueInfo.hasSupportedValuesList) {
toHardwarePropIdAreaIds.push_back(PropIdAreaId{.propId = propId, .areaId = areaId});
toHardwareRequestCounters.push_back(requestCounter);
@@ -1087,7 +1084,7 @@
continue;
}
- const auto& hasSupportedValueInfo = *(hasSupportedValueInfoResult.value());
+ const auto& hasSupportedValueInfo = hasSupportedValueInfoResult.value();
if (hasSupportedValueInfo.hasMinSupportedValue ||
hasSupportedValueInfo.hasMaxSupportedValue) {
toHardwarePropIdAreaIds.push_back(PropIdAreaId{.propId = propId, .areaId = areaId});
@@ -1130,15 +1127,65 @@
}
ScopedAStatus DefaultVehicleHal::registerSupportedValueChangeCallback(
- const std::shared_ptr<IVehicleCallback>&, const std::vector<VhalPropIdAreaId>&) {
- // TODO(b/381020465): Add relevant implementation.
- return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ const std::shared_ptr<IVehicleCallback>& callback,
+ const std::vector<VhalPropIdAreaId>& vhalPropIdAreaIds) {
+ std::vector<PropIdAreaId> propIdAreaIdsToSubscribe;
+ for (size_t i = 0; i < vhalPropIdAreaIds.size(); i++) {
+ const auto& vhalPropIdAreaId = vhalPropIdAreaIds.at(i);
+ int32_t propId = vhalPropIdAreaId.propId;
+ int32_t areaId = vhalPropIdAreaId.areaId;
+ auto hasSupportedValueInfoResult = getHasSupportedValueInfo(propId, areaId);
+ if (!hasSupportedValueInfoResult.ok()) {
+ ALOGE("registerSupportedValueChangeCallback not supported: %s",
+ hasSupportedValueInfoResult.error().message().c_str());
+ return toScopedAStatus(hasSupportedValueInfoResult, StatusCode::INVALID_ARG);
+ }
+ const auto& hasSupportedValueInfo = hasSupportedValueInfoResult.value();
+ if (!hasSupportedValueInfo.hasMinSupportedValue &&
+ !hasSupportedValueInfo.hasMaxSupportedValue &&
+ !hasSupportedValueInfo.hasSupportedValuesList) {
+ ALOGW("registerSupportedValueChangeCallback: do nothing for property: %s, "
+ "areaId: %" PRId32
+ ", no min/max supported values or supported values list"
+ " specified",
+ propIdToString(propId).c_str(), areaId);
+ continue;
+ }
+ propIdAreaIdsToSubscribe.push_back(PropIdAreaId{.propId = propId, .areaId = areaId});
+ }
+ if (propIdAreaIdsToSubscribe.empty()) {
+ return ScopedAStatus::ok();
+ }
+ auto result =
+ mSubscriptionManager->subscribeSupportedValueChange(callback, propIdAreaIdsToSubscribe);
+ if (!result.ok()) {
+ ALOGW("registerSupportedValueChangeCallback: failed to subscribe supported value change"
+ " for %s, error: %s",
+ fmt::format("{}", propIdAreaIdsToSubscribe).c_str(),
+ result.error().message().c_str());
+ return toScopedAStatus(result);
+ }
+ return ScopedAStatus::ok();
}
ScopedAStatus DefaultVehicleHal::unregisterSupportedValueChangeCallback(
- const std::shared_ptr<IVehicleCallback>&, const std::vector<VhalPropIdAreaId>&) {
- // TODO(b/381020465): Add relevant implementation.
- return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ const std::shared_ptr<IVehicleCallback>& callback,
+ const std::vector<VhalPropIdAreaId>& vhalPropIdAreaIds) {
+ std::vector<PropIdAreaId> propIdAreaIds;
+ for (const auto& vhalPropIdAreaId : vhalPropIdAreaIds) {
+ propIdAreaIds.push_back(
+ PropIdAreaId{.propId = vhalPropIdAreaId.propId, .areaId = vhalPropIdAreaId.areaId});
+ }
+
+ auto result = mSubscriptionManager->unsubscribeSupportedValueChange(callback->asBinder().get(),
+ propIdAreaIds);
+ if (!result.ok()) {
+ ALOGW("unregisterSupportedValueChangeCallback: failed to unsubscribe supported value change"
+ " for %s, error: %s",
+ fmt::format("{}", propIdAreaIds).c_str(), result.error().message().c_str());
+ return toScopedAStatus(result);
+ }
+ return ScopedAStatus::ok();
}
IVehicleHardware* DefaultVehicleHal::getHardware() {
@@ -1256,12 +1303,14 @@
dprintf(fd, "Currently have %zu getValues clients\n", mGetValuesClients.size());
dprintf(fd, "Currently have %zu setValues clients\n", mSetValuesClients.size());
dprintf(fd, "Currently have %zu subscribe clients\n", countSubscribeClients());
+ dprintf(fd, "Currently have %zu supported values change subscribe clients\n",
+ mSubscriptionManager->countSupportedValueChangeClients());
}
return STATUS_OK;
}
size_t DefaultVehicleHal::countSubscribeClients() {
- return mSubscriptionManager->countClients();
+ return mSubscriptionManager->countPropertyChangeClients();
}
} // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/current/vhal/src/SubscriptionManager.cpp b/automotive/vehicle/aidl/impl/current/vhal/src/SubscriptionManager.cpp
index 2d09e02..f790033 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/src/SubscriptionManager.cpp
+++ b/automotive/vehicle/aidl/impl/current/vhal/src/SubscriptionManager.cpp
@@ -17,6 +17,7 @@
#include "SubscriptionManager.h"
#include <VehicleUtils.h>
+#include <android-base/format.h>
#include <android-base/stringprintf.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
@@ -65,6 +66,8 @@
mClientsByPropIdAreaId.clear();
mSubscribedPropsByClient.clear();
+ mSupportedValueChangeClientsByPropIdAreaId.clear();
+ mSupportedValueChangePropIdAreaIdsByClient.clear();
}
bool SubscriptionManager::checkSampleRateHz(float sampleRateHz) {
@@ -166,8 +169,7 @@
/*enableVur*/ false));
status != StatusCode::OK) {
return StatusError(status)
- << StringPrintf("failed subscribe for prop: %s, areaId: %" PRId32,
- propIdToString(propId).c_str(), areaId);
+ << fmt::format("failed subscribe for propIdAreaId: {}", propIdAreaId);
}
return {};
}
@@ -379,16 +381,111 @@
if (mSubscribedPropsByClient.find(clientId) == mSubscribedPropsByClient.end()) {
ALOGW("No property was subscribed for this client, unsubscribe does nothing");
- return {};
+ } else {
+ auto& propIdAreaIds = mSubscribedPropsByClient[clientId];
+ for (auto const& propIdAreaId : propIdAreaIds) {
+ if (auto result = unsubscribePropIdAreaIdLocked(clientId, propIdAreaId); !result.ok()) {
+ return result;
+ }
+ }
+ mSubscribedPropsByClient.erase(clientId);
}
- auto& subscriptions = mSubscribedPropsByClient[clientId];
- for (auto const& propIdAreaId : subscriptions) {
- if (auto result = unsubscribePropIdAreaIdLocked(clientId, propIdAreaId); !result.ok()) {
+ if (mSupportedValueChangePropIdAreaIdsByClient.find(clientId) ==
+ mSupportedValueChangePropIdAreaIdsByClient.end()) {
+ ALOGW("No supported value change was subscribed for this client, unsubscribe does nothing");
+ } else {
+ const auto& propIdAreaIds = mSupportedValueChangePropIdAreaIdsByClient[clientId];
+ if (auto result = unsubscribeSupportedValueChangeLocked(
+ clientId,
+ std::vector<PropIdAreaId>(propIdAreaIds.begin(), propIdAreaIds.end()));
+ !result.ok()) {
return result;
}
}
- mSubscribedPropsByClient.erase(clientId);
+ return {};
+}
+
+VhalResult<void> SubscriptionManager::subscribeSupportedValueChange(
+ const std::shared_ptr<IVehicleCallback>& callback,
+ const std::vector<PropIdAreaId>& propIdAreaIds) {
+ // Need to make sure this whole operation is guarded by a lock so that our internal state is
+ // consistent with IVehicleHardware state.
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+
+ ClientIdType clientId = callback->asBinder().get();
+
+ // It is possible that some of the [propId, areaId]s are already subscribed, IVehicleHardware
+ // will ignore them.
+ if (auto status = mVehicleHardware->subscribeSupportedValueChange(propIdAreaIds);
+ status != StatusCode::OK) {
+ return StatusError(status)
+ << fmt::format("failed to call subscribeSupportedValueChange for propIdAreaIds: {}",
+ propIdAreaIds);
+ }
+ for (const auto& propIdAreaId : propIdAreaIds) {
+ mSupportedValueChangeClientsByPropIdAreaId[propIdAreaId][clientId] = callback;
+ // mSupportedValueChangePropIdAreaIdsByClient[clientId] is a set so this will ignore
+ // duplicate [propId, areaId].
+ mSupportedValueChangePropIdAreaIdsByClient[clientId].insert(propIdAreaId);
+ }
+ return {};
+}
+
+VhalResult<void> SubscriptionManager::unsubscribeSupportedValueChange(
+ SubscriptionManager::ClientIdType clientId,
+ const std::vector<PropIdAreaId>& propIdAreaIds) {
+ // Need to make sure this whole operation is guarded by a lock so that our internal state is
+ // consistent with IVehicleHardware state.
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+
+ return unsubscribeSupportedValueChangeLocked(clientId, propIdAreaIds);
+}
+
+VhalResult<void> SubscriptionManager::unsubscribeSupportedValueChangeLocked(
+ SubscriptionManager::ClientIdType clientId,
+ const std::vector<PropIdAreaId>& propIdAreaIds) {
+ std::vector<PropIdAreaId> propIdAreaIdsToUnsubscribe;
+
+ // Check which [propId, areaId] needs to be unsubscribed from the hardware.
+ for (const auto& propIdAreaId : propIdAreaIds) {
+ auto it = mSupportedValueChangeClientsByPropIdAreaId.find(propIdAreaId);
+ if (it != mSupportedValueChangeClientsByPropIdAreaId.end()) {
+ const auto& clients = it->second;
+ if (clients.size() == 1 && clients.find(clientId) != clients.end()) {
+ // This callback is the only client registered for [propId, areaId].
+ // Unregister it should unregister the [propId, areaId].
+ propIdAreaIdsToUnsubscribe.push_back(propIdAreaId);
+ }
+ }
+ }
+
+ // Send the unsubscribe request.
+ if (!propIdAreaIdsToUnsubscribe.empty()) {
+ if (auto status =
+ mVehicleHardware->unsubscribeSupportedValueChange(propIdAreaIdsToUnsubscribe);
+ status != StatusCode::OK) {
+ return StatusError(status) << fmt::format(
+ "failed to call unsubscribeSupportedValueChange for "
+ "propIdAreaIds: {}",
+ propIdAreaIdsToUnsubscribe);
+ }
+ }
+
+ // Remove internal book-keeping.
+ for (const auto& propIdAreaId : propIdAreaIds) {
+ if (mSupportedValueChangeClientsByPropIdAreaId.find(propIdAreaId) !=
+ mSupportedValueChangeClientsByPropIdAreaId.end()) {
+ mSupportedValueChangeClientsByPropIdAreaId[propIdAreaId].erase(clientId);
+ }
+ if (mSupportedValueChangeClientsByPropIdAreaId.empty()) {
+ mSupportedValueChangeClientsByPropIdAreaId.erase(propIdAreaId);
+ }
+ mSupportedValueChangePropIdAreaIdsByClient[clientId].erase(propIdAreaId);
+ if (mSupportedValueChangePropIdAreaIdsByClient[clientId].empty()) {
+ mSupportedValueChangePropIdAreaIdsByClient.erase(clientId);
+ }
+ }
return {};
}
@@ -486,14 +583,21 @@
bool SubscriptionManager::isEmpty() {
std::scoped_lock<std::mutex> lockGuard(mLock);
- return mSubscribedPropsByClient.empty() && mClientsByPropIdAreaId.empty();
+ return mSubscribedPropsByClient.empty() && mClientsByPropIdAreaId.empty() &&
+ mSupportedValueChangeClientsByPropIdAreaId.empty() &&
+ mSupportedValueChangePropIdAreaIdsByClient.empty();
}
-size_t SubscriptionManager::countClients() {
+size_t SubscriptionManager::countPropertyChangeClients() {
std::scoped_lock<std::mutex> lockGuard(mLock);
return mSubscribedPropsByClient.size();
}
+size_t SubscriptionManager::countSupportedValueChangeClients() {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ return mSupportedValueChangePropIdAreaIdsByClient.size();
+}
+
} // namespace vehicle
} // namespace automotive
} // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/current/vhal/test/DefaultVehicleHalTest.cpp b/automotive/vehicle/aidl/impl/current/vhal/test/DefaultVehicleHalTest.cpp
index 4df5ba3..0526f7d 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/test/DefaultVehicleHalTest.cpp
+++ b/automotive/vehicle/aidl/impl/current/vhal/test/DefaultVehicleHalTest.cpp
@@ -2418,6 +2418,205 @@
ASSERT_EQ(result.status, StatusCode::INVALID_ARG);
}
+TEST_F(DefaultVehicleHalTest, testRegisterSupportedValueChangeCallback) {
+ auto testConfigs = std::vector<VehiclePropConfig>(
+ {VehiclePropConfig{
+ .prop = testInt32VecProp(1),
+ .areaConfigs =
+ {
+ {.areaId = 0,
+ .hasSupportedValueInfo =
+ HasSupportedValueInfo{
+ .hasMinSupportedValue = false,
+ .hasMaxSupportedValue = false,
+ .hasSupportedValuesList = false,
+ }},
+ },
+ },
+ VehiclePropConfig{
+ .prop = testInt32VecWindowProp(2),
+ .areaConfigs =
+ {
+ {.areaId = 2,
+ .hasSupportedValueInfo =
+ HasSupportedValueInfo{
+ .hasMinSupportedValue = true,
+ .hasMaxSupportedValue = false,
+ .hasSupportedValuesList = false,
+ }},
+ },
+ }});
+
+ auto hardware = std::make_unique<MockVehicleHardware>();
+ MockVehicleHardware* hardwarePtr = hardware.get();
+ hardware->setPropertyConfigs(testConfigs);
+
+ auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+ std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+ // This request is ignored because it does not have supported value info.
+ auto propIdAreaId1 = VhalPropIdAreaId{.propId = testInt32VecProp(1), .areaId = 0};
+ auto propIdAreaId2 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 2};
+ auto status = client->registerSupportedValueChangeCallback(
+ getCallbackClient(), std::vector<VhalPropIdAreaId>{propIdAreaId1, propIdAreaId2});
+
+ ASSERT_TRUE(status.isOk()) << "Get non-okay status from registerSupportedValueChangeCallback"
+ << status.getMessage();
+ ASSERT_THAT(hardwarePtr->getSubscribedSupportedValueChangePropIdAreaIds(),
+ ElementsAre(PropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 2}));
+}
+
+TEST_F(DefaultVehicleHalTest, testRegisterSupportedValueChangeCallback_invalidRequest) {
+ auto testConfigs = std::vector<VehiclePropConfig>({VehiclePropConfig{
+ .prop = testInt32VecProp(1),
+ .areaConfigs =
+ {
+ {.areaId = 0, .hasSupportedValueInfo = std::nullopt},
+ },
+ }});
+ auto hardware = std::make_unique<MockVehicleHardware>();
+ hardware->setPropertyConfigs(testConfigs);
+
+ auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+ std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+ auto propIdAreaId1 = VhalPropIdAreaId{.propId = testInt32VecProp(1), .areaId = 0};
+ auto status = client->registerSupportedValueChangeCallback(
+ getCallbackClient(), std::vector<VhalPropIdAreaId>{propIdAreaId1});
+
+ ASSERT_FALSE(status.isOk()) << "registerSupportedValueChangeCallback must return error if one "
+ "of the requested [propId, areaId]"
+ " does not have supportedValueInfo";
+}
+
+TEST_F(DefaultVehicleHalTest, testRegisterSupportedValueChangeCallback_errorStatusFromHardware) {
+ auto testConfigs = std::vector<VehiclePropConfig>({VehiclePropConfig{
+ .prop = testInt32VecWindowProp(2),
+ .areaConfigs =
+ {
+ {.areaId = 2,
+ .hasSupportedValueInfo =
+ HasSupportedValueInfo{
+ .hasMinSupportedValue = true,
+ .hasMaxSupportedValue = false,
+ .hasSupportedValuesList = false,
+ }},
+ },
+ }});
+
+ auto hardware = std::make_unique<MockVehicleHardware>();
+ hardware->setStatus("subscribeSupportedValueChange", StatusCode::INTERNAL_ERROR);
+ hardware->setPropertyConfigs(testConfigs);
+
+ auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+ std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+ auto propIdAreaId = VhalPropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 2};
+ auto status = client->registerSupportedValueChangeCallback(
+ getCallbackClient(), std::vector<VhalPropIdAreaId>{propIdAreaId});
+
+ ASSERT_FALSE(status.isOk()) << "registerSupportedValueChangeCallback must return error if "
+ "VehicleHardware returns error";
+}
+
+TEST_F(DefaultVehicleHalTest, testUnregisterSupportedValueChangeCallback) {
+ auto testConfigs = std::vector<VehiclePropConfig>(
+ {VehiclePropConfig{
+ .prop = testInt32VecProp(1),
+ .areaConfigs =
+ {
+ {.areaId = 0,
+ .hasSupportedValueInfo =
+ HasSupportedValueInfo{
+ .hasMinSupportedValue = false,
+ .hasMaxSupportedValue = false,
+ .hasSupportedValuesList = true,
+ }},
+ },
+ },
+ VehiclePropConfig{
+ .prop = testInt32VecWindowProp(2),
+ .areaConfigs =
+ {
+ {.areaId = 2,
+ .hasSupportedValueInfo =
+ HasSupportedValueInfo{
+ .hasMinSupportedValue = true,
+ .hasMaxSupportedValue = false,
+ .hasSupportedValuesList = false,
+ }},
+ },
+ }});
+
+ auto hardware = std::make_unique<MockVehicleHardware>();
+ MockVehicleHardware* hardwarePtr = hardware.get();
+ hardware->setPropertyConfigs(testConfigs);
+
+ auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+ std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+ auto propIdAreaId1 = VhalPropIdAreaId{.propId = testInt32VecProp(1), .areaId = 0};
+ auto propIdAreaId2 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 2};
+ auto status = client->registerSupportedValueChangeCallback(
+ getCallbackClient(), std::vector<VhalPropIdAreaId>{propIdAreaId1, propIdAreaId2});
+
+ ASSERT_TRUE(status.isOk()) << "Get non-okay status from registerSupportedValueChangeCallback"
+ << status.getMessage();
+
+ status = client->unregisterSupportedValueChangeCallback(
+ getCallbackClient(), std::vector<VhalPropIdAreaId>{propIdAreaId1, propIdAreaId2});
+
+ ASSERT_TRUE(status.isOk()) << "Get non-okay status from unregisterSupportedValueChangeCallback"
+ << status.getMessage();
+
+ ASSERT_TRUE(hardwarePtr->getSubscribedSupportedValueChangePropIdAreaIds().empty())
+ << "All registered [propId, areaId]s must be unregistered";
+}
+
+TEST_F(DefaultVehicleHalTest, testUnregisterSupportedValueChangeCallback_ignoreUnregistered) {
+ auto testConfigs = std::vector<VehiclePropConfig>(
+ {VehiclePropConfig{
+ .prop = testInt32VecProp(1),
+ .areaConfigs =
+ {
+ {.areaId = 0,
+ .hasSupportedValueInfo =
+ HasSupportedValueInfo{
+ .hasMinSupportedValue = false,
+ .hasMaxSupportedValue = false,
+ .hasSupportedValuesList = true,
+ }},
+ },
+ },
+ VehiclePropConfig{
+ .prop = testInt32VecWindowProp(2),
+ .areaConfigs =
+ {
+ {.areaId = 2,
+ .hasSupportedValueInfo =
+ HasSupportedValueInfo{
+ .hasMinSupportedValue = true,
+ .hasMaxSupportedValue = false,
+ .hasSupportedValuesList = false,
+ }},
+ },
+ }});
+
+ auto hardware = std::make_unique<MockVehicleHardware>();
+ MockVehicleHardware* hardwarePtr = hardware.get();
+ hardware->setPropertyConfigs(testConfigs);
+
+ auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+ std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+ auto propIdAreaId1 = VhalPropIdAreaId{.propId = testInt32VecProp(1), .areaId = 0};
+ auto propIdAreaId2 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 2};
+ auto status = client->unregisterSupportedValueChangeCallback(
+ getCallbackClient(), std::vector<VhalPropIdAreaId>{propIdAreaId1, propIdAreaId2});
+
+ ASSERT_TRUE(status.isOk());
+}
+
} // namespace vehicle
} // namespace automotive
} // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.cpp b/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.cpp
index ae2b5a2..11f1835 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.cpp
@@ -310,6 +310,40 @@
return mEventBatchingWindow;
}
+StatusCode MockVehicleHardware::subscribeSupportedValueChange(
+ const std::vector<PropIdAreaId>& propIdAreaIds) {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ if (auto it = mStatusByFunctions.find(__func__); it != mStatusByFunctions.end()) {
+ if (StatusCode status = it->second; status != StatusCode::OK) {
+ return status;
+ }
+ }
+ for (const auto& propIdAreaId : propIdAreaIds) {
+ mSubscribedSupportedValueChangePropIdAreaIds.insert(propIdAreaId);
+ }
+ return StatusCode::OK;
+}
+
+StatusCode MockVehicleHardware::unsubscribeSupportedValueChange(
+ const std::vector<PropIdAreaId>& propIdAreaIds) {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ if (auto it = mStatusByFunctions.find(__func__); it != mStatusByFunctions.end()) {
+ if (StatusCode status = it->second; status != StatusCode::OK) {
+ return status;
+ }
+ }
+ for (const auto& propIdAreaId : propIdAreaIds) {
+ mSubscribedSupportedValueChangePropIdAreaIds.erase(propIdAreaId);
+ }
+ return StatusCode::OK;
+}
+
+std::unordered_set<PropIdAreaId, PropIdAreaIdHash>
+MockVehicleHardware::getSubscribedSupportedValueChangePropIdAreaIds() {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ return mSubscribedSupportedValueChangePropIdAreaIds;
+}
+
template <class ResultType>
StatusCode MockVehicleHardware::returnResponse(
std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback,
diff --git a/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.h b/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.h
index d1e9771..e7359db 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.h
@@ -71,6 +71,10 @@
getSupportedValuesLists(const std::vector<PropIdAreaId>& propIdAreaIds) override;
std::vector<aidl::android::hardware::automotive::vehicle::MinMaxSupportedValueResult>
getMinMaxSupportedValues(const std::vector<PropIdAreaId>& propIdAreaIds) override;
+ aidl::android::hardware::automotive::vehicle::StatusCode subscribeSupportedValueChange(
+ const std::vector<PropIdAreaId>& propIdAreaIds) override;
+ aidl::android::hardware::automotive::vehicle::StatusCode unsubscribeSupportedValueChange(
+ const std::vector<PropIdAreaId>& propIdAreaIds) override;
// Test functions.
void setPropertyConfigs(
@@ -114,10 +118,13 @@
std::vector<aidl::android::hardware::automotive::vehicle::SubscribeOptions>
getSubscribeOptions();
void clearSubscribeOptions();
- // Whether getAllPropertyConfigs() has been called, which blocks all all property configs
+ // Whether getAllPropertyConfigs() has been called, which blocks on all property configs
// being ready.
bool getAllPropertyConfigsCalled();
+ std::unordered_set<PropIdAreaId, PropIdAreaIdHash>
+ getSubscribedSupportedValueChangePropIdAreaIds();
+
private:
mutable std::mutex mLock;
mutable std::condition_variable mCv;
@@ -174,6 +181,9 @@
std::shared_ptr<RecurrentTimer> mRecurrentTimer;
std::unordered_map<int32_t, std::unordered_map<int32_t, std::shared_ptr<std::function<void()>>>>
mRecurrentActions GUARDED_BY(mLock);
+
+ std::unordered_set<PropIdAreaId, PropIdAreaIdHash> mSubscribedSupportedValueChangePropIdAreaIds
+ GUARDED_BY(mLock);
};
} // namespace vehicle
diff --git a/boot/aidl/default/Android.bp b/boot/aidl/default/Android.bp
index c1d3c57..2fd2dad 100644
--- a/boot/aidl/default/Android.bp
+++ b/boot/aidl/default/Android.bp
@@ -57,7 +57,7 @@
name: "android.hardware.boot-service.default_recovery",
defaults: ["android.hardware.boot-service_common"],
init_rc: ["android.hardware.boot-service.default_recovery.rc"],
- vintf_fragments: ["android.hardware.boot-service.default.xml"],
+ vintf_fragment_modules: ["android.hardware.boot-service.default.xml.recovery"],
recovery: true,
shared_libs: [
@@ -77,11 +77,16 @@
installable: false,
}
-prebuilt_etc {
+vintf_fragment {
+ name: "android.hardware.boot-service.default.xml.recovery",
+ src: "android.hardware.boot-service.default.xml",
+ recovery: true,
+}
+
+vintf_fragment {
name: "android.hardware.boot-service.default.xml",
src: "android.hardware.boot-service.default.xml",
- sub_dir: "vintf",
- installable: false,
+ vendor: true,
}
apex {
@@ -98,6 +103,8 @@
],
prebuilts: [
"android.hardware.boot-service.default.rc",
+ ],
+ vintf_fragment_modules: [
"android.hardware.boot-service.default.xml",
],
}
diff --git a/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IContextHub.aidl b/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IContextHub.aidl
index 2646d15..2940745 100644
--- a/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IContextHub.aidl
+++ b/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IContextHub.aidl
@@ -51,14 +51,6 @@
void sendMessageDeliveryStatusToHub(in int contextHubId, in android.hardware.contexthub.MessageDeliveryStatus messageDeliveryStatus);
List<android.hardware.contexthub.HubInfo> getHubs();
List<android.hardware.contexthub.EndpointInfo> getEndpoints();
- void registerEndpoint(in android.hardware.contexthub.EndpointInfo endpoint);
- void unregisterEndpoint(in android.hardware.contexthub.EndpointInfo endpoint);
- void registerEndpointCallback(in android.hardware.contexthub.IEndpointCallback callback);
- int[2] requestSessionIdRange(int size);
- void openEndpointSession(int sessionId, in android.hardware.contexthub.EndpointId destination, in android.hardware.contexthub.EndpointId initiator, in @nullable String serviceDescriptor);
- void sendMessageToEndpoint(int sessionId, in android.hardware.contexthub.Message msg);
- void sendMessageDeliveryStatusToEndpoint(int sessionId, in android.hardware.contexthub.MessageDeliveryStatus msgStatus);
- void closeEndpointSession(int sessionId, in android.hardware.contexthub.Reason reason);
- void endpointSessionOpenComplete(int sessionId);
+ @PropagateAllowBlocking android.hardware.contexthub.IEndpointCommunication registerEndpointHub(in android.hardware.contexthub.IEndpointCallback callback, in android.hardware.contexthub.HubInfo hubInfo);
const int EX_CONTEXT_HUB_UNSPECIFIED = (-1) /* -1 */;
}
diff --git a/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IEndpointCommunication.aidl b/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IEndpointCommunication.aidl
new file mode 100644
index 0000000..8742415
--- /dev/null
+++ b/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IEndpointCommunication.aidl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.contexthub;
+@VintfStability
+interface IEndpointCommunication {
+ void registerEndpoint(in android.hardware.contexthub.EndpointInfo endpoint);
+ void unregisterEndpoint(in android.hardware.contexthub.EndpointInfo endpoint);
+ int[2] requestSessionIdRange(int size);
+ void openEndpointSession(int sessionId, in android.hardware.contexthub.EndpointId destination, in android.hardware.contexthub.EndpointId initiator, in @nullable String serviceDescriptor);
+ void sendMessageToEndpoint(int sessionId, in android.hardware.contexthub.Message msg);
+ void sendMessageDeliveryStatusToEndpoint(int sessionId, in android.hardware.contexthub.MessageDeliveryStatus msgStatus);
+ void closeEndpointSession(int sessionId, in android.hardware.contexthub.Reason reason);
+ void endpointSessionOpenComplete(int sessionId);
+ void unregister();
+}
diff --git a/contexthub/aidl/android/hardware/contexthub/IContextHub.aidl b/contexthub/aidl/android/hardware/contexthub/IContextHub.aidl
index eb6d051..3fb452c 100644
--- a/contexthub/aidl/android/hardware/contexthub/IContextHub.aidl
+++ b/contexthub/aidl/android/hardware/contexthub/IContextHub.aidl
@@ -18,19 +18,16 @@
import android.hardware.contexthub.ContextHubInfo;
import android.hardware.contexthub.ContextHubMessage;
-import android.hardware.contexthub.EndpointId;
import android.hardware.contexthub.EndpointInfo;
import android.hardware.contexthub.HostEndpointInfo;
import android.hardware.contexthub.HubInfo;
import android.hardware.contexthub.IContextHubCallback;
import android.hardware.contexthub.IEndpointCallback;
-import android.hardware.contexthub.Message;
+import android.hardware.contexthub.IEndpointCommunication;
import android.hardware.contexthub.MessageDeliveryStatus;
import android.hardware.contexthub.NanSessionStateUpdate;
import android.hardware.contexthub.NanoappBinary;
import android.hardware.contexthub.NanoappInfo;
-import android.hardware.contexthub.Reason;
-import android.hardware.contexthub.Service;
import android.hardware.contexthub.Setting;
@VintfStability
@@ -271,125 +268,22 @@
List<EndpointInfo> getEndpoints();
/**
- * Publishes an endpoint from the calling side (e.g. Android). Endpoints must be registered
- * prior to starting a session.
+ * Registers a new hub for endpoint communication which will receive events for its endpoints
+ * over the given callback. Returns an interface for the hub to register endpoints, start
+ * sessions, and send messages.
+ *
+ * It is valid for the same callback to be registered for multiple hubs, as the
+ * IEndpointCallback events provide sufficient information to determine which hub the event is
+ * intended for:
+ * * session ids are allocated to a specific hub and are unique
+ * * endpoints are identified by hub and endpoint id
+ *
+ * @param callback Interface to send endpoint events targeting the caller
+ * @param hubInfo Details of the hub being registered
+ * @return Interface for the hub to interact with other endpoint hubs
+ *
+ * @throws EX_ILLEGAL_STATE if hubInfo.hubId has already been registered
*/
- void registerEndpoint(in EndpointInfo endpoint);
-
- /**
- * Teardown an endpoint from the calling side (e.g. Android). This endpoint must have already
- * been published via registerEndpoint().
- */
- void unregisterEndpoint(in EndpointInfo endpoint);
-
- /**
- * Attaches a callback interface to receive events targeted at endpoints registered by the
- * caller.
- */
- void registerEndpointCallback(in IEndpointCallback callback);
-
- /**
- * Request a range of session IDs for the caller to use when initiating sessions. This may be
- * called more than once, but typical usage is to request a large enough range to accommodate
- * the maximum expected number of concurrent sessions, but not overly large as to limit other
- * clients.
- *
- * @param size The number of sessionId reserved for host-initiated sessions. This number should
- * be less than or equal to 1024.
- *
- * @return An array with two elements representing the smallest and largest possible session id
- * available for host.
- *
- * @throws EX_ILLEGAL_ARGUMENT if the size is invalid.
- * @throws EX_SERVICE_SPECIFIC if the id range requested cannot be allocated.
- */
- int[2] requestSessionIdRange(int size);
-
- /**
- * Request to open a session for communication between an endpoint previously registered by the
- * caller and a target endpoint found in getEndpoints(), optionally scoped to a service
- * published by the target endpoint.
- *
- * Upon returning from this function, the session is in pending state, and the final result will
- * be given by an asynchronous call to onEndpointSessionOpenComplete() on success, or
- * onCloseEndpointSession() on failure.
- *
- * @param sessionId Caller-allocated session identifier, which must be unique across all active
- * sessions, and must fall in a range allocated via requestSessionIdRange().
- * @param destination The EndpointId representing the destination side of the session.
- * @param initiator The EndpointId representing the initiating side of the session, which
- * must've already been published through registerEndpoint().
- * @param serviceDescriptor Descriptor for the service specification for scoping this session
- * (nullable). Null indicates a fully custom marshalling scheme. The value should match
- * a published descriptor for both destination and initiator.
- *
- * @throws EX_ILLEGAL_ARGUMENT if any of the arguments are invalid, or the combination of the
- * arguments is invalid.
- * @throws EX_SERVICE_SPECIFIC on other errors
- * - EX_CONTEXT_HUB_UNSPECIFIED if the request failed for other reasons.
- */
- void openEndpointSession(int sessionId, in EndpointId destination, in EndpointId initiator,
- in @nullable String serviceDescriptor);
-
- /**
- * Send a message from one endpoint to another on the (currently open) session.
- *
- * @param sessionId The integer representing the communication session, previously set in
- * openEndpointSession() or onEndpointSessionOpenRequest().
- * @param msg The Message object representing a message to endpoint from the endpoint on host.
- *
- * @throws EX_ILLEGAL_ARGUMENT if any of the arguments are invalid, or the combination of the
- * arguments is invalid.
- * @throws EX_SERVICE_SPECIFIC on other errors
- * - EX_CONTEXT_HUB_UNSPECIFIED if the request failed for other reasons.
- */
- void sendMessageToEndpoint(int sessionId, in Message msg);
-
- /**
- * Sends a message delivery status to the endpoint in response to receiving a Message with flag
- * FLAG_REQUIRES_DELIVERY_STATUS. Each message with the flag should have a MessageDeliveryStatus
- * response. This method sends the message delivery status back to the remote endpoint for a
- * session.
- *
- * @param sessionId The integer representing the communication session, previously set in
- * openEndpointSession() or onEndpointSessionOpenRequest().
- * @param msgStatus The MessageDeliveryStatus object representing the delivery status for a
- * specific message (identified by the sequenceNumber) within the session.
- *
- * @throws EX_UNSUPPORTED_OPERATION if ContextHubInfo.supportsReliableMessages is false for
- * the hub involved in this session.
- */
- void sendMessageDeliveryStatusToEndpoint(int sessionId, in MessageDeliveryStatus msgStatus);
-
- /**
- * Closes a session previously opened by openEndpointSession() or requested via
- * onEndpointSessionOpenRequest(). Processing of session closure must be ordered/synchronized
- * with message delivery, such that if this session was open, any messages previously passed to
- * sendMessageToEndpoint() that are still in-flight must still be delivered before the session
- * is closed. Any in-flight messages to the endpoint that requested to close the session will
- * not be delivered.
- *
- * @param sessionId The integer representing the communication session, previously set in
- * openEndpointSession() or onEndpointSessionOpenRequest().
- * @param reason The reason for this close endpoint session request.
- *
- * @throws EX_ILLEGAL_ARGUMENT if any of the arguments are invalid, or the combination of the
- * arguments is invalid.
- * @throws EX_SERVICE_SPECIFIC on other errors
- * - EX_CONTEXT_HUB_UNSPECIFIED if the request failed for other reasons.
- */
- void closeEndpointSession(int sessionId, in Reason reason);
-
- /**
- * Notifies the HAL that the session requested by onEndpointSessionOpenRequest is ready to use.
- *
- * @param sessionId The integer representing the communication session, previously set in
- * onEndpointSessionOpenRequest(). This id is assigned by the HAL.
- *
- * @throws EX_ILLEGAL_ARGUMENT if any of the arguments are invalid, or the combination of the
- * arguments is invalid.
- * @throws EX_SERVICE_SPECIFIC on other errors
- * - EX_CONTEXT_HUB_UNSPECIFIED if the request failed for other reasons.
- */
- void endpointSessionOpenComplete(int sessionId);
+ @PropagateAllowBlocking
+ IEndpointCommunication registerEndpointHub(in IEndpointCallback callback, in HubInfo hubInfo);
}
diff --git a/contexthub/aidl/android/hardware/contexthub/IEndpointCommunication.aidl b/contexthub/aidl/android/hardware/contexthub/IEndpointCommunication.aidl
new file mode 100644
index 0000000..e5045ba
--- /dev/null
+++ b/contexthub/aidl/android/hardware/contexthub/IEndpointCommunication.aidl
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.contexthub;
+
+import android.hardware.contexthub.EndpointId;
+import android.hardware.contexthub.EndpointInfo;
+import android.hardware.contexthub.IEndpointCallback;
+import android.hardware.contexthub.Message;
+import android.hardware.contexthub.MessageDeliveryStatus;
+import android.hardware.contexthub.Reason;
+import android.hardware.contexthub.Service;
+
+@VintfStability
+interface IEndpointCommunication {
+ /**
+ * Publishes an endpoint from the calling side (e.g. Android). Endpoints must be registered
+ * prior to starting a session.
+ */
+ void registerEndpoint(in EndpointInfo endpoint);
+
+ /**
+ * Teardown an endpoint from the calling side (e.g. Android). This endpoint must have already
+ * been published via registerEndpoint().
+ */
+ void unregisterEndpoint(in EndpointInfo endpoint);
+
+ /**
+ * Request a range of session IDs for the caller to use when initiating sessions. This may be
+ * called more than once, but typical usage is to request a large enough range to accommodate
+ * the maximum expected number of concurrent sessions, but not overly large as to limit other
+ * clients.
+ *
+ * @param size The number of sessionId reserved for host-initiated sessions. This number should
+ * be less than or equal to 1024.
+ *
+ * @return An array with two elements representing the smallest and largest possible session id
+ * available for host.
+ *
+ * @throws EX_ILLEGAL_ARGUMENT if the size is invalid.
+ * @throws EX_SERVICE_SPECIFIC if the id range requested cannot be allocated.
+ */
+ int[2] requestSessionIdRange(int size);
+
+ /**
+ * Request to open a session for communication between an endpoint previously registered by the
+ * caller and a target endpoint found in getEndpoints(), optionally scoped to a service
+ * published by the target endpoint.
+ *
+ * Upon returning from this function, the session is in pending state, and the final result will
+ * be given by an asynchronous call to onEndpointSessionOpenComplete() on success, or
+ * onCloseEndpointSession() on failure.
+ *
+ * @param sessionId Caller-allocated session identifier, which must be unique across all active
+ * sessions, and must fall in a range allocated via requestSessionIdRange().
+ * @param destination The EndpointId representing the destination side of the session.
+ * @param initiator The EndpointId representing the initiating side of the session, which
+ * must've already been published through registerEndpoint().
+ * @param serviceDescriptor Descriptor for the service specification for scoping this session
+ * (nullable). Null indicates a fully custom marshalling scheme. The value should match
+ * a published descriptor for both destination and initiator.
+ *
+ * @throws EX_ILLEGAL_ARGUMENT if any of the arguments are invalid, or the combination of the
+ * arguments is invalid.
+ * @throws EX_SERVICE_SPECIFIC on other errors
+ * - EX_CONTEXT_HUB_UNSPECIFIED if the request failed for other reasons.
+ */
+ void openEndpointSession(int sessionId, in EndpointId destination, in EndpointId initiator,
+ in @nullable String serviceDescriptor);
+
+ /**
+ * Send a message from one endpoint to another on the (currently open) session.
+ *
+ * @param sessionId The integer representing the communication session, previously set in
+ * openEndpointSession() or onEndpointSessionOpenRequest().
+ * @param msg The Message object representing a message to endpoint from the endpoint on host.
+ *
+ * @throws EX_ILLEGAL_ARGUMENT if any of the arguments are invalid, or the combination of the
+ * arguments is invalid.
+ * @throws EX_SERVICE_SPECIFIC on other errors
+ * - EX_CONTEXT_HUB_UNSPECIFIED if the request failed for other reasons.
+ */
+ void sendMessageToEndpoint(int sessionId, in Message msg);
+
+ /**
+ * Sends a message delivery status to the endpoint in response to receiving a Message with flag
+ * FLAG_REQUIRES_DELIVERY_STATUS. Each message with the flag should have a MessageDeliveryStatus
+ * response. This method sends the message delivery status back to the remote endpoint for a
+ * session.
+ *
+ * @param sessionId The integer representing the communication session, previously set in
+ * openEndpointSession() or onEndpointSessionOpenRequest().
+ * @param msgStatus The MessageDeliveryStatus object representing the delivery status for a
+ * specific message (identified by the sequenceNumber) within the session.
+ *
+ * @throws EX_UNSUPPORTED_OPERATION if ContextHubInfo.supportsReliableMessages is false for
+ * the hub involved in this session.
+ */
+ void sendMessageDeliveryStatusToEndpoint(int sessionId, in MessageDeliveryStatus msgStatus);
+
+ /**
+ * Closes a session previously opened by openEndpointSession() or requested via
+ * onEndpointSessionOpenRequest(). Processing of session closure must be ordered/synchronized
+ * with message delivery, such that if this session was open, any messages previously passed to
+ * sendMessageToEndpoint() that are still in-flight must still be delivered before the session
+ * is closed. Any in-flight messages to the endpoint that requested to close the session will
+ * not be delivered.
+ *
+ * @param sessionId The integer representing the communication session, previously set in
+ * openEndpointSession() or onEndpointSessionOpenRequest().
+ * @param reason The reason for this close endpoint session request.
+ *
+ * @throws EX_ILLEGAL_ARGUMENT if any of the arguments are invalid, or the combination of the
+ * arguments is invalid.
+ * @throws EX_SERVICE_SPECIFIC on other errors
+ * - EX_CONTEXT_HUB_UNSPECIFIED if the request failed for other reasons.
+ */
+ void closeEndpointSession(int sessionId, in Reason reason);
+
+ /**
+ * Notifies the HAL that the session requested by onEndpointSessionOpenRequest is ready to use.
+ *
+ * @param sessionId The integer representing the communication session, previously set in
+ * onEndpointSessionOpenRequest(). This id is assigned by the HAL.
+ *
+ * @throws EX_ILLEGAL_ARGUMENT if any of the arguments are invalid, or the combination of the
+ * arguments is invalid.
+ * @throws EX_SERVICE_SPECIFIC on other errors
+ * - EX_CONTEXT_HUB_UNSPECIFIED if the request failed for other reasons.
+ */
+ void endpointSessionOpenComplete(int sessionId);
+
+ /**
+ * Unregisters this hub. Subsequent calls on this interface will fail.
+ *
+ * @throws EX_ILLEGAL_STATE if this interface was already unregistered.
+ */
+ void unregister();
+}
diff --git a/contexthub/aidl/default/ContextHub.cpp b/contexthub/aidl/default/ContextHub.cpp
index 19d9639..433617e 100644
--- a/contexthub/aidl/default/ContextHub.cpp
+++ b/contexthub/aidl/default/ContextHub.cpp
@@ -150,10 +150,11 @@
ScopedAStatus ContextHub::setTestMode(bool enable) {
if (enable) {
- std::unique_lock<std::mutex> lock(mEndpointMutex);
- mEndpoints.clear();
- mEndpointSessions.clear();
- mEndpointCallback = nullptr;
+ std::lock_guard lock(mHostHubsLock);
+ for (auto& [id, hub] : mIdToHostHub) {
+ hub->mActive = false;
+ }
+ mIdToHostHub.clear();
}
return ScopedAStatus::ok();
}
@@ -227,7 +228,23 @@
return ScopedAStatus::ok();
};
-ScopedAStatus ContextHub::registerEndpoint(const EndpointInfo& in_endpoint) {
+ScopedAStatus ContextHub::registerEndpointHub(
+ const std::shared_ptr<IEndpointCallback>& in_callback, const HubInfo& in_hubInfo,
+ std::shared_ptr<IEndpointCommunication>* _aidl_return) {
+ std::lock_guard lock(mHostHubsLock);
+ if (mIdToHostHub.count(in_hubInfo.hubId)) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ auto hub = ndk::SharedRefBase::make<HubInterface>(*this, in_callback, in_hubInfo);
+ mIdToHostHub.insert({in_hubInfo.hubId, hub});
+ *_aidl_return = std::move(hub);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus ContextHub::HubInterface::registerEndpoint(const EndpointInfo& in_endpoint) {
+ if (!mActive) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
std::unique_lock<std::mutex> lock(mEndpointMutex);
for (const EndpointInfo& endpoint : mEndpoints) {
@@ -240,7 +257,10 @@
return ScopedAStatus::ok();
};
-ScopedAStatus ContextHub::unregisterEndpoint(const EndpointInfo& in_endpoint) {
+ScopedAStatus ContextHub::HubInterface::unregisterEndpoint(const EndpointInfo& in_endpoint) {
+ if (!mActive) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
std::unique_lock<std::mutex> lock(mEndpointMutex);
for (auto it = mEndpoints.begin(); it != mEndpoints.end(); ++it) {
@@ -252,41 +272,47 @@
return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
};
-ScopedAStatus ContextHub::registerEndpointCallback(
- const std::shared_ptr<IEndpointCallback>& in_callback) {
- std::unique_lock<std::mutex> lock(mEndpointMutex);
-
- mEndpointCallback = in_callback;
- return ScopedAStatus::ok();
-};
-
-ScopedAStatus ContextHub::requestSessionIdRange(int32_t in_size,
- std::array<int32_t, 2>* _aidl_return) {
+ScopedAStatus ContextHub::HubInterface::requestSessionIdRange(
+ int32_t in_size, std::array<int32_t, 2>* _aidl_return) {
+ if (!mActive) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
constexpr int32_t kMaxSize = 1024;
if (in_size > kMaxSize || _aidl_return == nullptr) {
return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
+ uint16_t base = 0;
{
- std::lock_guard<std::mutex> lock(mEndpointMutex);
- mMaxValidSessionId = in_size;
+ std::lock_guard lock(mHal.mHostHubsLock);
+ if (static_cast<int32_t>(USHRT_MAX) - mHal.mNextSessionIdBase + 1 < in_size) {
+ return ScopedAStatus::fromServiceSpecificError(EX_CONTEXT_HUB_UNSPECIFIED);
+ }
+ base = mHal.mNextSessionIdBase;
+ mHal.mNextSessionIdBase += in_size;
}
- (*_aidl_return)[0] = 0;
- (*_aidl_return)[1] = in_size;
+ {
+ std::lock_guard<std::mutex> lock(mEndpointMutex);
+ (*_aidl_return)[0] = mBaseSessionId = base;
+ (*_aidl_return)[1] = mMaxSessionId = base + (in_size - 1);
+ }
return ScopedAStatus::ok();
};
-ScopedAStatus ContextHub::openEndpointSession(
+ScopedAStatus ContextHub::HubInterface::openEndpointSession(
int32_t in_sessionId, const EndpointId& in_destination, const EndpointId& in_initiator,
const std::optional<std::string>& in_serviceDescriptor) {
+ if (!mActive) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
// We are not calling onCloseEndpointSession on failure because the remote endpoints (our
// mock endpoints) always accept the session.
std::weak_ptr<IEndpointCallback> callback;
{
std::unique_lock<std::mutex> lock(mEndpointMutex);
- if (in_sessionId > mMaxValidSessionId) {
+ if (in_sessionId < mBaseSessionId || in_sessionId > mMaxSessionId) {
ALOGE("openEndpointSession: session ID %" PRId32 " is invalid", in_sessionId);
return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
@@ -346,7 +372,11 @@
return ScopedAStatus::ok();
};
-ScopedAStatus ContextHub::sendMessageToEndpoint(int32_t in_sessionId, const Message& in_msg) {
+ScopedAStatus ContextHub::HubInterface::sendMessageToEndpoint(int32_t in_sessionId,
+ const Message& in_msg) {
+ if (!mActive) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
std::weak_ptr<IEndpointCallback> callback;
{
std::unique_lock<std::mutex> lock(mEndpointMutex);
@@ -393,12 +423,19 @@
return ScopedAStatus::ok();
};
-ScopedAStatus ContextHub::sendMessageDeliveryStatusToEndpoint(
+ScopedAStatus ContextHub::HubInterface::sendMessageDeliveryStatusToEndpoint(
int32_t /* in_sessionId */, const MessageDeliveryStatus& /* in_msgStatus */) {
+ if (!mActive) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
return ScopedAStatus::ok();
};
-ScopedAStatus ContextHub::closeEndpointSession(int32_t in_sessionId, Reason /* in_reason */) {
+ScopedAStatus ContextHub::HubInterface::closeEndpointSession(int32_t in_sessionId,
+ Reason /* in_reason */) {
+ if (!mActive) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
std::unique_lock<std::mutex> lock(mEndpointMutex);
for (auto it = mEndpointSessions.begin(); it != mEndpointSessions.end(); ++it) {
@@ -411,8 +448,20 @@
return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
};
-ScopedAStatus ContextHub::endpointSessionOpenComplete(int32_t /* in_sessionId */) {
+ScopedAStatus ContextHub::HubInterface::endpointSessionOpenComplete(int32_t /* in_sessionId */) {
+ if (!mActive) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
return ScopedAStatus::ok();
};
+ScopedAStatus ContextHub::HubInterface::unregister() {
+ if (!mActive.exchange(false)) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ std::lock_guard lock(mHal.mHostHubsLock);
+ mHal.mIdToHostHub.erase(kInfo.hubId);
+ return ScopedAStatus::ok();
+}
+
} // namespace aidl::android::hardware::contexthub
diff --git a/contexthub/aidl/default/include/contexthub-impl/ContextHub.h b/contexthub/aidl/default/include/contexthub-impl/ContextHub.h
index 6da8bf2..65e84bb 100644
--- a/contexthub/aidl/default/include/contexthub-impl/ContextHub.h
+++ b/contexthub/aidl/default/include/contexthub-impl/ContextHub.h
@@ -17,7 +17,9 @@
#pragma once
#include <aidl/android/hardware/contexthub/BnContextHub.h>
+#include <aidl/android/hardware/contexthub/BnEndpointCommunication.h>
+#include <atomic>
#include <mutex>
#include <unordered_set>
#include <vector>
@@ -56,54 +58,79 @@
::ndk::ScopedAStatus getHubs(std::vector<HubInfo>* _aidl_return) override;
::ndk::ScopedAStatus getEndpoints(std::vector<EndpointInfo>* _aidl_return) override;
- ::ndk::ScopedAStatus registerEndpoint(const EndpointInfo& in_endpoint) override;
- ::ndk::ScopedAStatus unregisterEndpoint(const EndpointInfo& in_endpoint) override;
- ::ndk::ScopedAStatus registerEndpointCallback(
- const std::shared_ptr<IEndpointCallback>& in_callback) override;
- ::ndk::ScopedAStatus requestSessionIdRange(int32_t in_size,
- std::array<int32_t, 2>* _aidl_return) override;
- ::ndk::ScopedAStatus openEndpointSession(
- int32_t in_sessionId, const EndpointId& in_destination, const EndpointId& in_initiator,
- const std::optional<std::string>& in_serviceDescriptor) override;
- ::ndk::ScopedAStatus sendMessageToEndpoint(int32_t in_sessionId,
- const Message& in_msg) override;
- ::ndk::ScopedAStatus sendMessageDeliveryStatusToEndpoint(
- int32_t in_sessionId, const MessageDeliveryStatus& in_msgStatus) override;
- ::ndk::ScopedAStatus closeEndpointSession(int32_t in_sessionId, Reason in_reason) override;
- ::ndk::ScopedAStatus endpointSessionOpenComplete(int32_t in_sessionId) override;
+ ::ndk::ScopedAStatus registerEndpointHub(
+ const std::shared_ptr<IEndpointCallback>& in_callback, const HubInfo& in_hubInfo,
+ std::shared_ptr<IEndpointCommunication>* _aidl_return) override;
private:
- struct EndpointSession {
- int32_t sessionId;
- EndpointId initiator;
- EndpointId peer;
- std::optional<std::string> serviceDescriptor;
+ class HubInterface : public BnEndpointCommunication {
+ public:
+ HubInterface(ContextHub& hal, const std::shared_ptr<IEndpointCallback>& in_callback,
+ const HubInfo& in_hubInfo)
+ : mHal(hal), mEndpointCallback(in_callback), kInfo(in_hubInfo) {}
+ ~HubInterface() = default;
+
+ ::ndk::ScopedAStatus registerEndpoint(const EndpointInfo& in_endpoint) override;
+ ::ndk::ScopedAStatus unregisterEndpoint(const EndpointInfo& in_endpoint) override;
+ ::ndk::ScopedAStatus requestSessionIdRange(int32_t in_size,
+ std::array<int32_t, 2>* _aidl_return) override;
+ ::ndk::ScopedAStatus openEndpointSession(
+ int32_t in_sessionId, const EndpointId& in_destination,
+ const EndpointId& in_initiator,
+ const std::optional<std::string>& in_serviceDescriptor) override;
+ ::ndk::ScopedAStatus sendMessageToEndpoint(int32_t in_sessionId,
+ const Message& in_msg) override;
+ ::ndk::ScopedAStatus sendMessageDeliveryStatusToEndpoint(
+ int32_t in_sessionId, const MessageDeliveryStatus& in_msgStatus) override;
+ ::ndk::ScopedAStatus closeEndpointSession(int32_t in_sessionId, Reason in_reason) override;
+ ::ndk::ScopedAStatus endpointSessionOpenComplete(int32_t in_sessionId) override;
+ ::ndk::ScopedAStatus unregister() override;
+
+ private:
+ friend class ContextHub;
+
+ struct EndpointSession {
+ int32_t sessionId;
+ EndpointId initiator;
+ EndpointId peer;
+ std::optional<std::string> serviceDescriptor;
+ };
+
+ //! Finds an endpoint in the range defined by the endpoints
+ //! @return whether the endpoint was found
+ template <typename Iter>
+ bool findEndpoint(const EndpointId& target, const Iter& begin, const Iter& end) {
+ for (auto iter = begin; iter != end; ++iter) {
+ if (iter->id.id == target.id && iter->id.hubId == target.hubId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ //! Endpoint storage and information
+ ContextHub& mHal;
+ std::shared_ptr<IEndpointCallback> mEndpointCallback;
+ const HubInfo kInfo;
+
+ std::atomic<bool> mActive = true;
+
+ std::mutex mEndpointMutex;
+ std::vector<EndpointInfo> mEndpoints;
+ std::vector<EndpointSession> mEndpointSessions;
+ uint16_t mBaseSessionId;
+ uint16_t mMaxSessionId;
};
static constexpr uint32_t kMockHubId = 0;
- //! Finds an endpoint in the range defined by the endpoints
- //! @return whether the endpoint was found
- template <typename Iter>
- bool findEndpoint(const EndpointId& target, const Iter& begin, const Iter& end) {
- for (auto iter = begin; iter != end; ++iter) {
- if (iter->id.id == target.id && iter->id.hubId == target.hubId) {
- return true;
- }
- }
- return false;
- }
-
std::shared_ptr<IContextHubCallback> mCallback;
std::unordered_set<char16_t> mConnectedHostEndpoints;
- //! Endpoint storage and information
- std::mutex mEndpointMutex;
- std::vector<EndpointInfo> mEndpoints;
- std::vector<EndpointSession> mEndpointSessions;
- std::shared_ptr<IEndpointCallback> mEndpointCallback;
- int32_t mMaxValidSessionId = 0;
+ std::mutex mHostHubsLock;
+ std::unordered_map<int64_t, std::shared_ptr<HubInterface>> mIdToHostHub;
+ int32_t mNextSessionIdBase = 0;
};
} // namespace contexthub
diff --git a/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp b/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
index aa611ce..d7859d9 100644
--- a/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
+++ b/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
@@ -24,6 +24,7 @@
#include <android/hardware/contexthub/IContextHub.h>
#include <android/hardware/contexthub/IContextHubCallback.h>
#include <android/hardware/contexthub/IEndpointCallback.h>
+#include <android/hardware/contexthub/IEndpointCommunication.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <log/log.h>
@@ -46,6 +47,7 @@
using ::android::hardware::contexthub::HubInfo;
using ::android::hardware::contexthub::IContextHub;
using ::android::hardware::contexthub::IContextHubCallbackDefault;
+using ::android::hardware::contexthub::IEndpointCommunication;
using ::android::hardware::contexthub::Message;
using ::android::hardware::contexthub::MessageDeliveryStatus;
using ::android::hardware::contexthub::NanoappBinary;
@@ -62,34 +64,71 @@
// 6612b522-b717-41c8-b48d-c0b1cc64e142
constexpr std::array<uint8_t, 16> kUuid = {0x66, 0x12, 0xb5, 0x22, 0xb7, 0x17, 0x41, 0xc8,
0xb4, 0x8d, 0xc0, 0xb1, 0xcc, 0x64, 0xe1, 0x42};
+
const String16 kName{"VtsAidlHalContextHubTargetTest"};
const String16 kEchoServiceName{"android.hardware.contexthub.test.EchoService"};
+constexpr int64_t kDefaultHubId = 1;
+
+class TestEndpointCallback;
class ContextHubAidl : public testing::TestWithParam<std::tuple<std::string, int32_t>> {
public:
- virtual void SetUp() override {
- contextHub = android::waitForDeclaredService<IContextHub>(
+ void SetUp() override {
+ mContextHub = android::waitForDeclaredService<IContextHub>(
String16(std::get<0>(GetParam()).c_str()));
- ASSERT_NE(contextHub, nullptr);
-
- // Best effort enable test mode - this may not be supported on older HALS, so we
- // ignore the return value.
- contextHub->setTestMode(/* enable= */ true);
+ ASSERT_NE(mContextHub, nullptr);
+ mEndpointCb = sp<TestEndpointCallback>::make();
}
- virtual void TearDown() override { contextHub->setTestMode(/* enable= */ false); }
-
uint32_t getHubId() { return std::get<1>(GetParam()); }
+ Status registerHub(int64_t id, sp<IEndpointCommunication>* hubInterface) {
+ HubInfo info;
+ info.hubId = id;
+ return mContextHub->registerEndpointHub(mEndpointCb, info, hubInterface);
+ }
+
+ bool registerDefaultHub() {
+ Status status = registerHub(kDefaultHubId, &mHubInterface);
+ if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
+ status.transactionError() == android::UNKNOWN_TRANSACTION) {
+ return false;
+ }
+ EXPECT_TRUE(status.isOk());
+ EXPECT_NE(mHubInterface, nullptr);
+ if (!mHubInterface) {
+ return false;
+ }
+ return true;
+ }
+
void testSettingChanged(Setting setting);
- sp<IContextHub> contextHub;
+ sp<IContextHub> mContextHub;
+ sp<TestEndpointCallback> mEndpointCb;
+ sp<IEndpointCommunication> mHubInterface;
+};
+
+class ContextHubAidlWithTestMode : public ContextHubAidl {
+ public:
+ void SetUp() override {
+ ContextHubAidl::SetUp();
+
+ // Best effort enable test mode - this may not be supported on older HALS, so we
+ // ignore the return value.
+ mContextHub->setTestMode(/* enable= */ true);
+ }
+
+ void TearDown() override {
+ mContextHub->setTestMode(/* enable= */ false);
+ ContextHubAidl::TearDown();
+ }
};
TEST_P(ContextHubAidl, TestGetHubs) {
std::vector<ContextHubInfo> hubs;
- ASSERT_TRUE(contextHub->getContextHubs(&hubs).isOk());
+ ASSERT_TRUE(mContextHub->getContextHubs(&hubs).isOk());
ALOGD("System reports %zu hubs", hubs.size());
@@ -111,7 +150,7 @@
}
TEST_P(ContextHubAidl, TestEnableTestMode) {
- Status status = contextHub->setTestMode(true);
+ Status status = mContextHub->setTestMode(true);
if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
status.transactionError() == android::UNKNOWN_TRANSACTION) {
GTEST_SKIP() << "Not supported -> old API; or not implemented";
@@ -121,7 +160,7 @@
}
TEST_P(ContextHubAidl, TestDisableTestMode) {
- Status status = contextHub->setTestMode(false);
+ Status status = mContextHub->setTestMode(false);
if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
status.transactionError() == android::UNKNOWN_TRANSACTION) {
GTEST_SKIP() << "Not supported -> old API; or not implemented";
@@ -170,7 +209,7 @@
TEST_P(ContextHubAidl, TestRegisterCallback) {
sp<EmptyContextHubCallback> cb = sp<EmptyContextHubCallback>::make();
- ASSERT_TRUE(contextHub->registerCallback(getHubId(), cb).isOk());
+ ASSERT_TRUE(mContextHub->registerCallback(getHubId(), cb).isOk());
}
// Helper callback that puts the async appInfo callback data into a promise
@@ -219,8 +258,8 @@
// Calls queryApps() and checks the returned metadata
TEST_P(ContextHubAidl, TestQueryApps) {
sp<QueryAppsCallback> cb = sp<QueryAppsCallback>::make();
- ASSERT_TRUE(contextHub->registerCallback(getHubId(), cb).isOk());
- ASSERT_TRUE(contextHub->queryNanoapps(getHubId()).isOk());
+ ASSERT_TRUE(mContextHub->registerCallback(getHubId(), cb).isOk());
+ ASSERT_TRUE(mContextHub->queryNanoapps(getHubId()).isOk());
std::vector<NanoappInfo> appInfoList;
ASSERT_TRUE(waitForCallback(cb->promise.get_future(), &appInfoList));
@@ -241,7 +280,7 @@
// Calls getPreloadedNanoappsIds() and verifies there are preloaded nanoapps
TEST_P(ContextHubAidl, TestGetPreloadedNanoappIds) {
std::vector<int64_t> preloadedNanoappIds;
- Status status = contextHub->getPreloadedNanoappIds(getHubId(), &preloadedNanoappIds);
+ Status status = mContextHub->getPreloadedNanoappIds(getHubId(), &preloadedNanoappIds);
if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
status.transactionError() == android::UNKNOWN_TRANSACTION) {
GTEST_SKIP() << "Not supported -> old API; or not implemented";
@@ -304,7 +343,7 @@
public:
virtual void SetUp() override {
ContextHubAidl::SetUp();
- ASSERT_TRUE(contextHub->registerCallback(getHubId(), cb).isOk());
+ ASSERT_TRUE(mContextHub->registerCallback(getHubId(), cb).isOk());
}
sp<TransactionResultCallback> cb = sp<TransactionResultCallback>::make();
@@ -318,7 +357,7 @@
std::fill(message.messageBody.begin(), message.messageBody.end(), 0);
ALOGD("Sending message to non-existent nanoapp");
- ASSERT_TRUE(contextHub->sendMessageToHub(getHubId(), message).isOk());
+ ASSERT_TRUE(mContextHub->sendMessageToHub(getHubId(), message).isOk());
}
TEST_P(ContextHubTransactionTest, TestLoadEmptyNanoapp) {
@@ -332,7 +371,7 @@
emptyApp.targetChreApiMinorVersion = 0;
ALOGD("Loading empty nanoapp");
- bool success = contextHub->loadNanoapp(getHubId(), emptyApp, cb->expectedTransactionId).isOk();
+ bool success = mContextHub->loadNanoapp(getHubId(), emptyApp, cb->expectedTransactionId).isOk();
if (success) {
bool transactionSuccess;
ASSERT_TRUE(waitForCallback(cb->promise.get_future(), &transactionSuccess));
@@ -345,7 +384,7 @@
ALOGD("Unloading nonexistent nanoapp");
bool success =
- contextHub->unloadNanoapp(getHubId(), kNonExistentAppId, cb->expectedTransactionId)
+ mContextHub->unloadNanoapp(getHubId(), kNonExistentAppId, cb->expectedTransactionId)
.isOk();
if (success) {
bool transactionSuccess;
@@ -359,7 +398,7 @@
ALOGD("Enabling nonexistent nanoapp");
bool success =
- contextHub->enableNanoapp(getHubId(), kNonExistentAppId, cb->expectedTransactionId)
+ mContextHub->enableNanoapp(getHubId(), kNonExistentAppId, cb->expectedTransactionId)
.isOk();
if (success) {
bool transactionSuccess;
@@ -373,7 +412,7 @@
ALOGD("Disabling nonexistent nanoapp");
bool success =
- contextHub->disableNanoapp(getHubId(), kNonExistentAppId, cb->expectedTransactionId)
+ mContextHub->disableNanoapp(getHubId(), kNonExistentAppId, cb->expectedTransactionId)
.isOk();
if (success) {
bool transactionSuccess;
@@ -386,10 +425,10 @@
// In VTS, we only test that sending the values doesn't cause things to blow up - GTS tests
// verify the expected E2E behavior in CHRE
sp<EmptyContextHubCallback> cb = sp<EmptyContextHubCallback>::make();
- ASSERT_TRUE(contextHub->registerCallback(getHubId(), cb).isOk());
+ ASSERT_TRUE(mContextHub->registerCallback(getHubId(), cb).isOk());
- ASSERT_TRUE(contextHub->onSettingChanged(setting, true /* enabled */).isOk());
- ASSERT_TRUE(contextHub->onSettingChanged(setting, false /* enabled */).isOk());
+ ASSERT_TRUE(mContextHub->onSettingChanged(setting, true /* enabled */).isOk());
+ ASSERT_TRUE(mContextHub->onSettingChanged(setting, false /* enabled */).isOk());
}
TEST_P(ContextHubAidl, TestOnLocationSettingChanged) {
@@ -444,27 +483,27 @@
hostEndpointInfo.type = HostEndpointInfo::Type::NATIVE;
hostEndpointInfo.hostEndpointId = kHostEndpointId;
- ASSERT_TRUE(contextHub->onHostEndpointConnected(hostEndpointInfo).isOk());
- ASSERT_TRUE(contextHub->onHostEndpointDisconnected(kHostEndpointId).isOk());
+ ASSERT_TRUE(mContextHub->onHostEndpointConnected(hostEndpointInfo).isOk());
+ ASSERT_TRUE(mContextHub->onHostEndpointDisconnected(kHostEndpointId).isOk());
}
TEST_P(ContextHubTransactionTest, TestInvalidHostConnection) {
constexpr char16_t kHostEndpointId = 1;
- ASSERT_TRUE(contextHub->onHostEndpointDisconnected(kHostEndpointId).isOk());
+ ASSERT_TRUE(mContextHub->onHostEndpointDisconnected(kHostEndpointId).isOk());
}
TEST_P(ContextHubTransactionTest, TestNanSessionStateChange) {
NanSessionStateUpdate update;
update.state = true;
- Status status = contextHub->onNanSessionStateChanged(update);
+ Status status = mContextHub->onNanSessionStateChanged(update);
if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
status.transactionError() == android::UNKNOWN_TRANSACTION) {
GTEST_SKIP() << "Not supported -> old API; or not implemented";
} else {
ASSERT_TRUE(status.isOk());
update.state = false;
- ASSERT_TRUE(contextHub->onNanSessionStateChanged(update).isOk());
+ ASSERT_TRUE(mContextHub->onNanSessionStateChanged(update).isOk());
}
}
@@ -473,7 +512,7 @@
messageDeliveryStatus.messageSequenceNumber = 123;
messageDeliveryStatus.errorCode = ErrorCode::OK;
- Status status = contextHub->sendMessageDeliveryStatusToHub(getHubId(), messageDeliveryStatus);
+ Status status = mContextHub->sendMessageDeliveryStatusToHub(getHubId(), messageDeliveryStatus);
if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
status.transactionError() == android::UNKNOWN_TRANSACTION) {
GTEST_SKIP() << "Not supported -> old API; or not implemented";
@@ -546,7 +585,30 @@
bool mWasOnEndpointSessionOpenCompleteCalled = false;
};
-TEST_P(ContextHubAidl, RegisterEndpoint) {
+TEST_P(ContextHubAidlWithTestMode, RegisterHub) {
+ if (!registerDefaultHub()) {
+ GTEST_SKIP() << "Not supported -> old API; or not implemented";
+ }
+
+ sp<IEndpointCommunication> hub2;
+ Status status = registerHub(kDefaultHubId + 1, &hub2);
+ EXPECT_TRUE(status.isOk());
+
+ sp<IEndpointCommunication> hub3;
+ status = registerHub(kDefaultHubId + 1, &hub3);
+ ASSERT_FALSE(status.isOk());
+ EXPECT_EQ(status.exceptionCode(), Status::EX_ILLEGAL_STATE);
+
+ hub2->unregister();
+ status = registerHub(kDefaultHubId + 1, &hub3);
+ EXPECT_TRUE(status.isOk());
+}
+
+TEST_P(ContextHubAidlWithTestMode, RegisterEndpoint) {
+ if (!registerDefaultHub()) {
+ GTEST_SKIP() << "Not supported -> old API; or not implemented";
+ }
+
EndpointInfo endpointInfo;
endpointInfo.id.id = 1;
endpointInfo.id.hubId = 0xCAFECAFECAFECAFE;
@@ -554,7 +616,7 @@
endpointInfo.name = String16("Test host endpoint 1");
endpointInfo.version = 42;
- Status status = contextHub->registerEndpoint(endpointInfo);
+ Status status = mHubInterface->registerEndpoint(endpointInfo);
if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
status.transactionError() == android::UNKNOWN_TRANSACTION) {
GTEST_SKIP() << "Not supported -> old API; or not implemented";
@@ -563,7 +625,11 @@
}
}
-TEST_P(ContextHubAidl, RegisterEndpointSameNameFailure) {
+TEST_P(ContextHubAidlWithTestMode, RegisterEndpointSameNameFailure) {
+ if (!registerDefaultHub()) {
+ GTEST_SKIP() << "Not supported -> old API; or not implemented";
+ }
+
EndpointInfo endpointInfo;
endpointInfo.id.id = 2;
endpointInfo.id.hubId = 0xCAFECAFECAFECAFE;
@@ -578,7 +644,7 @@
endpointInfo2.name = String16("Test host endpoint 2");
endpointInfo2.version = 42;
- Status status = contextHub->registerEndpoint(endpointInfo);
+ Status status = mHubInterface->registerEndpoint(endpointInfo);
if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
status.transactionError() == android::UNKNOWN_TRANSACTION) {
GTEST_SKIP() << "Not supported -> old API; or not implemented";
@@ -586,10 +652,14 @@
EXPECT_TRUE(status.isOk());
}
- EXPECT_FALSE(contextHub->registerEndpoint(endpointInfo2).isOk());
+ EXPECT_FALSE(mHubInterface->registerEndpoint(endpointInfo2).isOk());
}
-TEST_P(ContextHubAidl, RegisterEndpointSameIdFailure) {
+TEST_P(ContextHubAidlWithTestMode, RegisterEndpointSameIdFailure) {
+ if (!registerDefaultHub()) {
+ GTEST_SKIP() << "Not supported -> old API; or not implemented";
+ }
+
EndpointInfo endpointInfo;
endpointInfo.id.id = 4;
endpointInfo.id.hubId = 0xCAFECAFECAFECAFE;
@@ -604,7 +674,7 @@
endpointInfo2.name = String16("Test host endpoint - same ID test");
endpointInfo2.version = 42;
- Status status = contextHub->registerEndpoint(endpointInfo);
+ Status status = mHubInterface->registerEndpoint(endpointInfo);
if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
status.transactionError() == android::UNKNOWN_TRANSACTION) {
GTEST_SKIP() << "Not supported -> old API; or not implemented";
@@ -612,10 +682,14 @@
EXPECT_TRUE(status.isOk());
}
- EXPECT_FALSE(contextHub->registerEndpoint(endpointInfo2).isOk());
+ EXPECT_FALSE(mHubInterface->registerEndpoint(endpointInfo2).isOk());
}
-TEST_P(ContextHubAidl, UnregisterEndpoint) {
+TEST_P(ContextHubAidlWithTestMode, UnregisterEndpoint) {
+ if (!registerDefaultHub()) {
+ GTEST_SKIP() << "Not supported -> old API; or not implemented";
+ }
+
EndpointInfo endpointInfo;
endpointInfo.id.id = 6;
endpointInfo.id.hubId = 0xCAFECAFECAFECAFE;
@@ -623,7 +697,7 @@
endpointInfo.name = String16("Test host endpoint 6");
endpointInfo.version = 42;
- Status status = contextHub->registerEndpoint(endpointInfo);
+ Status status = mHubInterface->registerEndpoint(endpointInfo);
if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
status.transactionError() == android::UNKNOWN_TRANSACTION) {
GTEST_SKIP() << "Not supported -> old API; or not implemented";
@@ -631,10 +705,14 @@
EXPECT_TRUE(status.isOk());
}
- EXPECT_TRUE(contextHub->unregisterEndpoint(endpointInfo).isOk());
+ EXPECT_TRUE(mHubInterface->unregisterEndpoint(endpointInfo).isOk());
}
-TEST_P(ContextHubAidl, UnregisterEndpointNonexistent) {
+TEST_P(ContextHubAidlWithTestMode, UnregisterEndpointNonexistent) {
+ if (!registerDefaultHub()) {
+ GTEST_SKIP() << "Not supported -> old API; or not implemented";
+ }
+
EndpointInfo endpointInfo;
endpointInfo.id.id = 100;
endpointInfo.id.hubId = 0xCAFECAFECAFECAFE;
@@ -642,7 +720,7 @@
endpointInfo.name = String16("Test host endpoint 100");
endpointInfo.version = 42;
- Status status = contextHub->unregisterEndpoint(endpointInfo);
+ Status status = mHubInterface->unregisterEndpoint(endpointInfo);
if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
status.transactionError() == android::UNKNOWN_TRANSACTION) {
GTEST_SKIP() << "Not supported -> old API; or not implemented";
@@ -651,25 +729,9 @@
}
}
-TEST_P(ContextHubAidl, RegisterCallback) {
- auto cb = sp<TestEndpointCallback>::make();
- Status status = contextHub->registerEndpointCallback(cb);
- if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
- status.transactionError() == android::UNKNOWN_TRANSACTION) {
+TEST_P(ContextHubAidlWithTestMode, OpenEndpointSessionInvalidRange) {
+ if (!registerDefaultHub()) {
GTEST_SKIP() << "Not supported -> old API; or not implemented";
- } else {
- EXPECT_TRUE(status.isOk());
- }
-}
-
-TEST_P(ContextHubAidl, OpenEndpointSessionInvalidRange) {
- auto cb = sp<TestEndpointCallback>::make();
- Status status = contextHub->registerEndpointCallback(cb);
- if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
- status.transactionError() == android::UNKNOWN_TRANSACTION) {
- GTEST_SKIP() << "Not supported -> old API; or not implemented";
- } else {
- EXPECT_TRUE(status.isOk());
}
// Register the endpoint
@@ -679,11 +741,11 @@
initiatorEndpoint.type = EndpointInfo::EndpointType::NATIVE;
initiatorEndpoint.name = String16("Test host endpoint 7");
initiatorEndpoint.version = 42;
- EXPECT_TRUE(contextHub->registerEndpoint(initiatorEndpoint).isOk());
+ EXPECT_TRUE(mHubInterface->registerEndpoint(initiatorEndpoint).isOk());
// Find the destination, if it exists
std::vector<EndpointInfo> endpoints;
- EXPECT_TRUE(contextHub->getEndpoints(&endpoints).isOk());
+ EXPECT_TRUE(mContextHub->getEndpoints(&endpoints).isOk());
const EndpointInfo* destinationEndpoint = nullptr;
for (const EndpointInfo& endpoint : endpoints) {
for (const Service& service : endpoint.services) {
@@ -700,30 +762,24 @@
// Request the range
constexpr int32_t requestedRange = 100;
std::array<int32_t, 2> range;
- ASSERT_TRUE(contextHub->requestSessionIdRange(requestedRange, &range).isOk());
+ ASSERT_TRUE(mHubInterface->requestSessionIdRange(requestedRange, &range).isOk());
EXPECT_EQ(range.size(), 2);
EXPECT_GE(range[1] - range[0] + 1, requestedRange);
// Open the session
int32_t sessionId = range[1] + 10; // invalid
- EXPECT_FALSE(contextHub
+ EXPECT_FALSE(mHubInterface
->openEndpointSession(sessionId, destinationEndpoint->id,
initiatorEndpoint.id,
/* in_serviceDescriptor= */ kEchoServiceName)
.isOk());
}
-TEST_P(ContextHubAidl, OpenEndpointSessionAndSendMessageEchoesBack) {
- auto cb = sp<TestEndpointCallback>::make();
- Status status = contextHub->registerEndpointCallback(cb);
- if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
- status.transactionError() == android::UNKNOWN_TRANSACTION) {
+TEST_P(ContextHubAidlWithTestMode, OpenEndpointSessionAndSendMessageEchoesBack) {
+ if (!registerDefaultHub()) {
GTEST_SKIP() << "Not supported -> old API; or not implemented";
- } else {
- EXPECT_TRUE(status.isOk());
}
-
- std::unique_lock<std::mutex> lock(cb->getMutex());
+ std::unique_lock<std::mutex> lock(mEndpointCb->getMutex());
// Register the endpoint
EndpointInfo initiatorEndpoint;
@@ -732,11 +788,11 @@
initiatorEndpoint.type = EndpointInfo::EndpointType::NATIVE;
initiatorEndpoint.name = String16("Test host endpoint 7");
initiatorEndpoint.version = 42;
- EXPECT_TRUE(contextHub->registerEndpoint(initiatorEndpoint).isOk());
+ EXPECT_TRUE(mHubInterface->registerEndpoint(initiatorEndpoint).isOk());
// Find the destination, if it exists
std::vector<EndpointInfo> endpoints;
- EXPECT_TRUE(contextHub->getEndpoints(&endpoints).isOk());
+ EXPECT_TRUE(mContextHub->getEndpoints(&endpoints).isOk());
const EndpointInfo* destinationEndpoint = nullptr;
for (const EndpointInfo& endpoint : endpoints) {
for (const Service& service : endpoint.services) {
@@ -753,32 +809,32 @@
// Request the range
constexpr int32_t requestedRange = 100;
std::array<int32_t, 2> range;
- ASSERT_TRUE(contextHub->requestSessionIdRange(requestedRange, &range).isOk());
+ ASSERT_TRUE(mHubInterface->requestSessionIdRange(requestedRange, &range).isOk());
EXPECT_EQ(range.size(), 2);
EXPECT_GE(range[1] - range[0] + 1, requestedRange);
// Open the session
- cb->resetWasOnEndpointSessionOpenCompleteCalled();
+ mEndpointCb->resetWasOnEndpointSessionOpenCompleteCalled();
int32_t sessionId = range[0];
- ASSERT_TRUE(contextHub
+ ASSERT_TRUE(mHubInterface
->openEndpointSession(sessionId, destinationEndpoint->id,
initiatorEndpoint.id,
/* in_serviceDescriptor= */ kEchoServiceName)
.isOk());
- cb->getCondVar().wait(lock);
- EXPECT_TRUE(cb->wasOnEndpointSessionOpenCompleteCalled());
+ mEndpointCb->getCondVar().wait(lock);
+ EXPECT_TRUE(mEndpointCb->wasOnEndpointSessionOpenCompleteCalled());
// Send the message
Message message;
message.flags = 0;
message.sequenceNumber = 0;
message.content.push_back(42);
- ASSERT_TRUE(contextHub->sendMessageToEndpoint(sessionId, message).isOk());
+ ASSERT_TRUE(mHubInterface->sendMessageToEndpoint(sessionId, message).isOk());
// Check for echo
- cb->getCondVar().wait(lock);
- EXPECT_FALSE(cb->getMessages().empty());
- EXPECT_EQ(cb->getMessages().back().content.back(), 42);
+ mEndpointCb->getCondVar().wait(lock);
+ EXPECT_FALSE(mEndpointCb->getMessages().empty());
+ EXPECT_EQ(mEndpointCb->getMessages().back().content.back(), 42);
}
std::string PrintGeneratedTest(const testing::TestParamInfo<ContextHubAidl::ParamType>& info) {
@@ -789,13 +845,17 @@
INSTANTIATE_TEST_SUITE_P(ContextHub, ContextHubAidl, testing::ValuesIn(generateContextHubMapping()),
PrintGeneratedTest);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ContextHubAidlWithTestMode);
+INSTANTIATE_TEST_SUITE_P(ContextHub, ContextHubAidlWithTestMode,
+ testing::ValuesIn(generateContextHubMapping()), PrintGeneratedTest);
+
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ContextHubTransactionTest);
INSTANTIATE_TEST_SUITE_P(ContextHub, ContextHubTransactionTest,
testing::ValuesIn(generateContextHubMapping()), PrintGeneratedTest);
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
- ProcessState::self()->setThreadPoolMaxThreadCount(1);
+ ProcessState::self()->setThreadPoolMaxThreadCount(2);
ProcessState::self()->startThreadPool();
return RUN_ALL_TESTS();
}
diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp
index eaeec32..5afaf31 100644
--- a/security/keymint/support/remote_prov_utils.cpp
+++ b/security/keymint/support/remote_prov_utils.cpp
@@ -870,7 +870,7 @@
}
auto csr = hwtrust::Csr::validate(encodedCsr, *diceChainKind, false /*isFactory*/,
- false /*allowAnyMode*/, deviceSuffix(instanceName));
+ true /*allowAnyMode*/, deviceSuffix(instanceName));
if (!csr.ok()) {
return csr.error().message();
}
@@ -904,7 +904,7 @@
}
auto csr1 = hwtrust::Csr::validate(encodedCsr1, *diceChainKind, false /*isFactory*/,
- false /*allowAnyMode*/, deviceSuffix(instanceName1));
+ true /*allowAnyMode*/, deviceSuffix(instanceName1));
if (!csr1.ok()) {
return csr1.error().message();
}
@@ -921,7 +921,7 @@
}
auto csr2 = hwtrust::Csr::validate(encodedCsr2, *diceChainKind, false /*isFactory*/,
- false /*allowAnyMode*/, deviceSuffix(instanceName2));
+ true /*allowAnyMode*/, deviceSuffix(instanceName2));
if (!csr2.ok()) {
return csr2.error().message();
}
@@ -952,7 +952,7 @@
}
auto csr = hwtrust::Csr::validate(encodedCsr, *diceChainKind, false /*isFactory*/,
- false /*allowAnyMode*/, deviceSuffix(DEFAULT_INSTANCE_NAME));
+ true /*allowAnyMode*/, deviceSuffix(DEFAULT_INSTANCE_NAME));
if (!csr.ok()) {
return csr.error().message();
}
diff --git a/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see.hwcrypto/current/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl b/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see.hwcrypto/current/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl
index b31a06c..99eb761 100644
--- a/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see.hwcrypto/current/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl
+++ b/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see.hwcrypto/current/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl
@@ -64,7 +64,7 @@
int keySizeBytes;
}
union DerivedKeyPolicy {
- android.hardware.security.see.hwcrypto.IHwCryptoKey.ClearKeyPolicy clearKey;
+ android.hardware.security.see.hwcrypto.IHwCryptoKey.ClearKeyPolicy clearKeyPolicy;
byte[] opaqueKey;
}
parcelable DerivedKeyParameters {
diff --git a/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see.hwcrypto/current/android/hardware/security/see/hwcrypto/IHwCryptoOperations.aidl b/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see.hwcrypto/current/android/hardware/security/see/hwcrypto/IHwCryptoOperations.aidl
index 7c87dd3..3adb2f9 100644
--- a/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see.hwcrypto/current/android/hardware/security/see/hwcrypto/IHwCryptoOperations.aidl
+++ b/security/see/hwcrypto/aidl/aidl_api/android.hardware.security.see.hwcrypto/current/android/hardware/security/see/hwcrypto/IHwCryptoOperations.aidl
@@ -34,5 +34,5 @@
package android.hardware.security.see.hwcrypto;
@VintfStability
interface IHwCryptoOperations {
- android.hardware.security.see.hwcrypto.CryptoOperationResult[] processCommandList(inout android.hardware.security.see.hwcrypto.CryptoOperationSet[] operations, out android.hardware.security.see.hwcrypto.CryptoOperationErrorAdditionalInfo additionalErrorInfo);
+ android.hardware.security.see.hwcrypto.CryptoOperationResult[] processCommandList(inout android.hardware.security.see.hwcrypto.CryptoOperationSet[] operations);
}
diff --git a/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl b/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl
index 97a4c37..93d6cbc 100644
--- a/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl
+++ b/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IHwCryptoKey.aidl
@@ -107,10 +107,12 @@
* If used we will derive a clear key and pass it back as an array of bytes on
* <code>HwCryptoKeyMaterial::explicitKey</code>.
*/
- ClearKeyPolicy clearKey;
+ ClearKeyPolicy clearKeyPolicy;
/*
* Policy for the newly derived opaque key. Defines how the key can be used and its type.
+ * Its definition can be found in <code>KeyPolicy.cddl</code>, which is basically a CBOR
+ * serialization of the file <code>KeyPolicy.aidl</code>.
*/
byte[] opaqueKey;
}
@@ -154,11 +156,14 @@
* Key to be used to derive the new key using HKDF.
*
* @return:
- * A DiceCurrentBoundKeyResult containint the versioned key tied the current client version
+ * A DiceCurrentBoundKeyResult containing the versioned key tied the current client version
* on success.
*
* @throws:
- * ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs.
+ * ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs,
+ * in particular:
+ * - BAD_PARAMETER if an invalid DeviceKeyId is requested.
+ * - INVALID_KEY if an opaque key is provided that is not suitable for key derivation.
*/
DiceCurrentBoundKeyResult deriveCurrentDicePolicyBoundKey(
in DiceBoundDerivationKey derivationKey);
@@ -184,7 +189,11 @@
* success.
*
* @throws:
- * ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs.
+ * ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs,
+ * in particular:
+ * - BAD_PARAMETER if an invalid DeviceKeyId is requested or if dicePolicyForKeyVersion
+ * is not a valid encrypted DICE policy.
+ * - INVALID_KEY if an opaque key is provided that is not suitable for key derivation.
*/
DiceBoundKeyResult deriveDicePolicyBoundKey(
in DiceBoundDerivationKey derivationKey, in byte[] dicePolicyForKeyVersion);
@@ -197,10 +206,15 @@
* file for more information.
*
* @return:
- * A HwCryptoKeyMaterial containing the derived key on success.
+ * A <code>DerivedKey</code> containing the derived key on success.
*
* @throws:
- * ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs.
+ * ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs,
+ * in particular:
+ * - BAD_PARAMETER if an invalid key policy is provided or if the key policy conflicts
+ * with the requested key.
+ * - SERIALIZATION_ERROR if the provided key policy is not a valid CBOR key policy.
+ * - INVALID_KEY if an opaque key is provided that is not suitable for key derivation.
*/
DerivedKey deriveKey(in DerivedKeyParameters parameters);
@@ -233,7 +247,11 @@
* IOpaqueKey on success.
*
* @throws:
- * ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs.
+ * ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs,
+ * in particular:
+ * - BAD_PARAMETER if an invalid Key policy is provided or if the key policy conflicts
+ * with provided key material.
+ * - ALLOCATION_ERROR if the system runs out of memory while carring out the operation.
*/
IOpaqueKey importClearKey(in ExplicitKeyMaterial keyMaterial, in KeyPolicy newKeyPolicy);
@@ -248,7 +266,9 @@
* passing the receiver DICE policy to insure that only that receiver can import the key.
*
* @return:
- * byte[] on success, which is the caller encrypted DICE policy.
+ * byte[] on success, which is the caller encrypted DICE policy. The DICE policy follows
+ * the structure defined on DicePolicy.cddl, located under
+ * hardware/interfaces/security/authgraph/aidl/android/hardware/security/authgraph/
*/
byte[] getCurrentDicePolicy();
@@ -266,10 +286,14 @@
* DICE policy used to seal the exported key.
*
* @return:
- * An IOpaqueKey that can be directly be used on the local HWCrypto service on success.
+ * An IOpaqueKey that can be directly used on the local HWCrypto service on success.
*
* @throws:
- * ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs.
+ * ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs,
+ * in particular:
+ * - BAD_PARAMETER if an invalid encrypted sealing DICE policy is provided.
+ * - ALLOCATION_ERROR if the system runs out of memory while carring out the operation.
+ * - UNAUTHORIZED if the sealingDicePolicy do not match the caller.
*/
IOpaqueKey keyTokenImport(in OpaqueKeyToken requestedKey, in byte[] sealingDicePolicy);
@@ -287,8 +311,9 @@
* An IOpaqueKey corresponding to the requested key slot on success.
*
* @throws:
- * ServiceSpecificException <code>UNAUTHORIZED</code> if the caller cannot access the
- * requested key, another specific error based on <code>HalErrorCode</code> otherwise.
+ * ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs,
+ * in particular:
+ * - UNAUTHORIZED if the caller cannot access the requested key.
*/
IOpaqueKey getKeyslotData(KeySlot slotId);
}
diff --git a/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IHwCryptoOperations.aidl b/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IHwCryptoOperations.aidl
index 9df6d67..dbe4d80 100644
--- a/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IHwCryptoOperations.aidl
+++ b/security/see/hwcrypto/aidl/android/hardware/security/see/hwcrypto/IHwCryptoOperations.aidl
@@ -31,19 +31,21 @@
*
* @param operations:
* Parameter containing 1 or more set of commands to execute. Additionally, each set can
- * also contain a context on which the commands will be executed.
- *
- * @param additionalErrorInfo:
- * Structure containing additional info when errors are encountered. Only valid if the
- * function failed its execution.
+ * also contain a context on which the commands will be executed. The parameter has type
+ * inout because it can contain buffers used to write the output of the operation.
*
* @return:
* CryptoOperationResult[] on success, which can contain a context to continue executing
* each of the provided operations sets.
*
* @throws:
- * ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs.
+ * ServiceSpecificException based on <code>HalErrorCode</code> if any error occurs,
+ * in particular:
+ * - INVALID_KEY if the provided key is not compatible with the operation requested.
+ * - BAD_STATE if the provided <code>CryptoOperationSet</code> contains operations that
+ * cannot be carried out in the current server state.
+ * - UNSUPPORTED if the requested operation is not supported by the server.
+ * - ALLOCATION_ERROR if the system runs out of memory while carring out the operation.
*/
- CryptoOperationResult[] processCommandList(inout CryptoOperationSet[] operations,
- out CryptoOperationErrorAdditionalInfo additionalErrorInfo);
+ CryptoOperationResult[] processCommandList(inout CryptoOperationSet[] operations);
}
diff --git a/tv/mediaquality/aidl/vts/functional/VtsHalMediaQualityTest.cpp b/tv/mediaquality/aidl/vts/functional/VtsHalMediaQualityTest.cpp
index f785cad..a01d4b0 100644
--- a/tv/mediaquality/aidl/vts/functional/VtsHalMediaQualityTest.cpp
+++ b/tv/mediaquality/aidl/vts/functional/VtsHalMediaQualityTest.cpp
@@ -35,6 +35,7 @@
#include <android/binder_manager.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
+#include <log/log.h>
#include <future>
using aidl::android::hardware::graphics::common::PixelFormat;
@@ -63,6 +64,407 @@
#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
+void validateParameterRange0To100(int value) {
+ EXPECT_GE(value, 0);
+ EXPECT_LE(value, 100);
+}
+
+void validateParameterRange0To2047(int value) {
+ EXPECT_GE(value, 0);
+ EXPECT_LE(value, 2047);
+}
+
+void validateColorTemperature(int value) {
+ EXPECT_GE(value, -100);
+ EXPECT_LE(value, 100);
+}
+
+void validatePictureParameter(const PictureParameter& param) {
+ switch (param.getTag()) {
+ case PictureParameter::Tag::brightness: {
+ ALOGD("[validatePictureParameter] validate brightness value");
+ float value = param.get<PictureParameter::Tag::brightness>();
+ EXPECT_TRUE(value >= 0.0f && value <= 1.0f);
+ break;
+ }
+ case PictureParameter::Tag::contrast: {
+ ALOGD("[validatePictureParameter] validate contrast value");
+ int value = param.get<PictureParameter::Tag::contrast>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::sharpness: {
+ ALOGD("[validatePictureParameter] validate sharpness value");
+ int value = param.get<PictureParameter::Tag::sharpness>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::saturation: {
+ ALOGD("[validatePictureParameter] validate saturation value");
+ int value = param.get<PictureParameter::Tag::saturation>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::hue: {
+ ALOGD("[validatePictureParameter] validate hue value");
+ int value = param.get<PictureParameter::Tag::hue>();
+ EXPECT_GE(value, -50);
+ EXPECT_LE(value, 50);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerBrightness: {
+ ALOGD("[validatePictureParameter] validate colorTunerBrightness value");
+ int value = param.get<PictureParameter::Tag::colorTunerBrightness>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerSaturation: {
+ ALOGD("[validatePictureParameter] validate colorTunerSaturation value");
+ int value = param.get<PictureParameter::Tag::colorTunerSaturation>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerHue: {
+ ALOGD("[validatePictureParameter] validate colorTunerHue value");
+ int value = param.get<PictureParameter::Tag::colorTunerHue>();
+ EXPECT_GE(value, -50);
+ EXPECT_LE(value, 50);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerRedOffset: {
+ ALOGD("[validatePictureParameter] validate colorTunerRedOffset value");
+ int value = param.get<PictureParameter::Tag::colorTunerRedOffset>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerGreenOffset: {
+ ALOGD("[validatePictureParameter] validate colorTunerGreenOffset value");
+ int value = param.get<PictureParameter::Tag::colorTunerGreenOffset>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerBlueOffset: {
+ ALOGD("[validatePictureParameter] validate colorTunerBlueOffset value");
+ int value = param.get<PictureParameter::Tag::colorTunerBlueOffset>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerRedGain: {
+ ALOGD("[validatePictureParameter] validate colorTunerRedGain value");
+ int value = param.get<PictureParameter::Tag::colorTunerRedGain>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerGreenGain: {
+ ALOGD("[validatePictureParameter] validate colorTunerGreenGain value");
+ int value = param.get<PictureParameter::Tag::colorTunerGreenGain>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerBlueGain: {
+ ALOGD("[validatePictureParameter] validate colorTunerBlueGain value");
+ int value = param.get<PictureParameter::Tag::colorTunerBlueGain>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::panelInitMaxLuminceNits: {
+ ALOGD("[validatePictureParameter] validate panelInitMaxLuminceNits value");
+ int value = param.get<PictureParameter::Tag::panelInitMaxLuminceNits>();
+ EXPECT_GE(value, 0);
+ EXPECT_LE(value, 10000);
+ break;
+ }
+ case PictureParameter::Tag::colorTemperatureRedGain: {
+ ALOGD("[validatePictureParameter] validate colorTemperatureRedGain value");
+ int value = param.get<PictureParameter::Tag::colorTemperatureRedGain>();
+ validateColorTemperature(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTemperatureGreenGain: {
+ ALOGD("[validatePictureParameter] validate colorTemperatureGreenGain value");
+ int value = param.get<PictureParameter::Tag::colorTemperatureGreenGain>();
+ validateColorTemperature(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTemperatureBlueGain: {
+ ALOGD("[validatePictureParameter] validate colorTemperatureBlueGain value");
+ int value = param.get<PictureParameter::Tag::colorTemperatureBlueGain>();
+ validateColorTemperature(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTemperatureRedOffset: {
+ ALOGD("[validatePictureParameter] validate ccolorTemperatureRedOffset value");
+ int value = param.get<PictureParameter::Tag::colorTemperatureRedOffset>();
+ validateColorTemperature(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTemperatureGreenOffset: {
+ ALOGD("[validatePictureParameter] validate colorTemperatureGreenOffset value");
+ int value = param.get<PictureParameter::Tag::colorTemperatureGreenOffset>();
+ validateColorTemperature(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTemperatureBlueOffset: {
+ ALOGD("[validatePictureParameter] validate colorTemperatureBlueOffset value");
+ int value = param.get<PictureParameter::Tag::colorTemperatureBlueOffset>();
+ validateColorTemperature(value);
+ break;
+ }
+ case PictureParameter::Tag::elevenPointRed: {
+ ALOGD("[validatePictureParameter] validate elevenPointRed value");
+ std::array<int, 11> elevenPointValues =
+ param.get<PictureParameter::Tag::elevenPointRed>();
+ for (int value : elevenPointValues) {
+ validateParameterRange0To100(value);
+ }
+ break;
+ }
+ case PictureParameter::Tag::elevenPointGreen: {
+ ALOGD("[validatePictureParameter] validate elevenPointGreen value");
+ std::array<int, 11> elevenPointValues =
+ param.get<PictureParameter::Tag::elevenPointGreen>();
+ for (int value : elevenPointValues) {
+ validateParameterRange0To100(value);
+ }
+ break;
+ }
+ case PictureParameter::Tag::elevenPointBlue: {
+ ALOGD("[validatePictureParameter] validate elevenPointBlue value");
+ std::array<int, 11> elevenPointValues =
+ param.get<PictureParameter::Tag::elevenPointBlue>();
+ for (int value : elevenPointValues) {
+ validateParameterRange0To100(value);
+ }
+ break;
+ }
+ case PictureParameter::Tag::osdRedGain: {
+ ALOGD("[validatePictureParameter] validate osdRedGain value");
+ int value = param.get<PictureParameter::Tag::osdRedGain>();
+ validateParameterRange0To2047(value);
+ break;
+ }
+ case PictureParameter::Tag::osdGreenGain: {
+ ALOGD("[validatePictureParameter] validate osdGreenGain value");
+ int value = param.get<PictureParameter::Tag::osdGreenGain>();
+ validateParameterRange0To2047(value);
+ break;
+ }
+ case PictureParameter::Tag::osdBlueGain: {
+ ALOGD("[validatePictureParameter] validate osdBlueGain value");
+ int value = param.get<PictureParameter::Tag::osdBlueGain>();
+ validateParameterRange0To2047(value);
+ break;
+ }
+ case PictureParameter::Tag::osdRedOffset: {
+ ALOGD("[validatePictureParameter] validate osdRedOffset value");
+ int value = param.get<PictureParameter::Tag::osdRedOffset>();
+ validateParameterRange0To2047(value);
+ break;
+ }
+ case PictureParameter::Tag::osdGreenOffset: {
+ ALOGD("[validatePictureParameter] validate osdGreenOffset value");
+ int value = param.get<PictureParameter::Tag::osdGreenOffset>();
+ validateParameterRange0To2047(value);
+ break;
+ }
+ case PictureParameter::Tag::osdBlueOffset: {
+ ALOGD("[validatePictureParameter] validate osdBlueOffset value");
+ int value = param.get<PictureParameter::Tag::osdBlueOffset>();
+ validateParameterRange0To2047(value);
+ break;
+ }
+ case PictureParameter::Tag::osdHue: {
+ ALOGD("[validatePictureParameter] validate osdHue value");
+ int value = param.get<PictureParameter::Tag::osdHue>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::osdSaturation: {
+ ALOGD("[validatePictureParameter] validate osdSaturation value");
+ int value = param.get<PictureParameter::Tag::osdSaturation>();
+ EXPECT_GE(value, 0);
+ EXPECT_LE(value, 255);
+ break;
+ }
+ case PictureParameter::Tag::osdContrast: {
+ ALOGD("[validatePictureParameter] validate osdContrast value");
+ int value = param.get<PictureParameter::Tag::osdContrast>();
+ validateParameterRange0To2047(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerHueRed: {
+ ALOGD("[validatePictureParameter] validate colorTunerHueRed value");
+ int value = param.get<PictureParameter::Tag::colorTunerHueRed>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerHueGreen: {
+ ALOGD("[validatePictureParameter] validate colorTunerHueGreen value");
+ int value = param.get<PictureParameter::Tag::colorTunerHueGreen>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerHueBlue: {
+ ALOGD("[validatePictureParameter] validate colorTunerHueBlue value");
+ int value = param.get<PictureParameter::Tag::colorTunerHueBlue>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerHueCyan: {
+ ALOGD("[validatePictureParameter] validate colorTunerHueCyan value");
+ int value = param.get<PictureParameter::Tag::colorTunerHueCyan>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerHueMagenta: {
+ ALOGD("[validatePictureParameter] validate colorTunerHueMagenta value");
+ int value = param.get<PictureParameter::Tag::colorTunerHueMagenta>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerHueYellow: {
+ ALOGD("[validatePictureParameter] validate colorTunerHueYellow value");
+ int value = param.get<PictureParameter::Tag::colorTunerHueYellow>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerHueFlesh: {
+ ALOGD("[validatePictureParameter] validate colorTunerHueFlesh value");
+ int value = param.get<PictureParameter::Tag::colorTunerHueFlesh>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerSaturationRed: {
+ ALOGD("[validatePictureParameter] validate colorTunerSaturationRed value");
+ int value = param.get<PictureParameter::Tag::colorTunerSaturationRed>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerSaturationGreen: {
+ ALOGD("[validatePictureParameter] validate colorTunerSaturationGreen value");
+ int value = param.get<PictureParameter::Tag::colorTunerSaturationGreen>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerSaturationBlue: {
+ ALOGD("[validatePictureParameter] validate colorTunerSaturationBlue value");
+ int value = param.get<PictureParameter::Tag::colorTunerSaturationBlue>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerSaturationCyan: {
+ ALOGD("[validatePictureParameter] validate colorTunerSaturationCyan value");
+ int value = param.get<PictureParameter::Tag::colorTunerSaturationCyan>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerSaturationMagenta: {
+ ALOGD("[validatePictureParameter] validate colorTunerSaturationMagenta value");
+ int value = param.get<PictureParameter::Tag::colorTunerSaturationMagenta>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerSaturationYellow: {
+ ALOGD("[validatePictureParameter] validate colorTunerSaturationYellow value");
+ int value = param.get<PictureParameter::Tag::colorTunerSaturationYellow>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerSaturationFlesh: {
+ ALOGD("[validatePictureParameter] validate colorTunerSaturationFlesh value");
+ int value = param.get<PictureParameter::Tag::colorTunerSaturationFlesh>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerLuminanceRed: {
+ ALOGD("[validatePictureParameter] validate colorTunerLuminanceRed value");
+ int value = param.get<PictureParameter::Tag::colorTunerLuminanceRed>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerLuminanceGreen: {
+ ALOGD("[validatePictureParameter] validate colorTunerLuminanceGreen value");
+ int value = param.get<PictureParameter::Tag::colorTunerLuminanceGreen>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerLuminanceBlue: {
+ ALOGD("[validatePictureParameter] validate colorTunerLuminanceBlue value");
+ int value = param.get<PictureParameter::Tag::colorTunerLuminanceBlue>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerLuminanceCyan: {
+ ALOGD("[validatePictureParameter] validate colorTunerLuminanceCyan value");
+ int value = param.get<PictureParameter::Tag::colorTunerLuminanceCyan>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerLuminanceMagenta: {
+ ALOGD("[validatePictureParameter] validate colorTunerLuminanceMagenta value");
+ int value = param.get<PictureParameter::Tag::colorTunerLuminanceMagenta>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerLuminanceYellow: {
+ ALOGD("[validatePictureParameter] validate colorTunerLuminanceYellow value");
+ int value = param.get<PictureParameter::Tag::colorTunerLuminanceYellow>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case PictureParameter::Tag::colorTunerLuminanceFlesh: {
+ ALOGD("[validatePictureParameter] validate colorTunerLuminanceFlesh value");
+ int value = param.get<PictureParameter::Tag::colorTunerLuminanceFlesh>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ default:
+ ALOGD("Those parameters don't need to check.");
+ break;
+ }
+}
+
+void validateSoundParameter(const SoundParameter& param) {
+ switch (param.getTag()) {
+ case SoundParameter::Tag::balance: {
+ ALOGD("[validateSoundParameter] validate balance value");
+ int value = param.get<SoundParameter::Tag::balance>();
+ EXPECT_GE(value, -50);
+ EXPECT_LE(value, 50);
+ break;
+ }
+ case SoundParameter::Tag::bass: {
+ ALOGD("[validateSoundParameter] validate bass value");
+ int value = param.get<SoundParameter::Tag::bass>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case SoundParameter::Tag::treble: {
+ ALOGD("[validateSoundParameter] validate treble value");
+ int value = param.get<SoundParameter::Tag::treble>();
+ validateParameterRange0To100(value);
+ break;
+ }
+ case SoundParameter::Tag::speakersDelayMs: {
+ ALOGD("[validateSoundParameter] validate speakersDelayMs value");
+ int value = param.get<SoundParameter::Tag::speakersDelayMs>();
+ EXPECT_GE(value, 0);
+ EXPECT_LE(value, 250);
+ break;
+ }
+ case SoundParameter::Tag::digitalOutputDelayMs: {
+ ALOGD("[validateSoundParameter] validate digitalOutputDelayMs value");
+ int value = param.get<SoundParameter::Tag::digitalOutputDelayMs>();
+ EXPECT_GE(value, 0);
+ EXPECT_LE(value, 250);
+ break;
+ }
+ default:
+ ALOGD("Those parameters don't need to check.");
+ break;
+ }
+}
+
class MediaQualityCallback : public BnMediaQualityCallback {
public:
explicit MediaQualityCallback(
@@ -83,7 +485,11 @@
const std::function<void(const PictureProfile& pictureProfile)>&
on_hal_picture_profile_adjust)
: on_hal_picture_profile_adjust_(on_hal_picture_profile_adjust) {}
+
ScopedAStatus onPictureProfileAdjusted(const PictureProfile& pictureProfile) override {
+ for (const auto& param : pictureProfile.parameters.pictureParameters) {
+ validatePictureParameter(param);
+ }
on_hal_picture_profile_adjust_(pictureProfile);
return ScopedAStatus::ok();
}
@@ -111,7 +517,11 @@
const std::function<void(const SoundProfile& soundProfile)>&
on_hal_sound_profile_adjust)
: on_hal_sound_profile_adjust_(on_hal_sound_profile_adjust) {}
+
ScopedAStatus onSoundProfileAdjusted(const SoundProfile& soundProfile) override {
+ for (const auto& param : soundProfile.parameters.soundParameters) {
+ validateSoundParameter(param);
+ }
on_hal_sound_profile_adjust_(soundProfile);
return ScopedAStatus::ok();
}
diff --git a/wifi/aidl/vts/functional/wifi_rtt_controller_aidl_test.cpp b/wifi/aidl/vts/functional/wifi_rtt_controller_aidl_test.cpp
index 1596602..aca1364 100644
--- a/wifi/aidl/vts/functional/wifi_rtt_controller_aidl_test.cpp
+++ b/wifi/aidl/vts/functional/wifi_rtt_controller_aidl_test.cpp
@@ -19,8 +19,10 @@
#include <VtsCoreUtil.h>
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
+#include <aidl/android/hardware/wifi/Akm.h>
#include <aidl/android/hardware/wifi/BnWifi.h>
#include <aidl/android/hardware/wifi/BnWifiRttControllerEventCallback.h>
+#include <aidl/android/hardware/wifi/RttSecureConfig.h>
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_status.h>
@@ -29,6 +31,7 @@
#include "wifi_aidl_test_utils.h"
+using aidl::android::hardware::wifi::Akm;
using aidl::android::hardware::wifi::BnWifiRttControllerEventCallback;
using aidl::android::hardware::wifi::IWifiRttController;
using aidl::android::hardware::wifi::RttBw;
@@ -38,6 +41,7 @@
using aidl::android::hardware::wifi::RttPreamble;
using aidl::android::hardware::wifi::RttResponder;
using aidl::android::hardware::wifi::RttResult;
+using aidl::android::hardware::wifi::RttSecureConfig;
using aidl::android::hardware::wifi::RttType;
using aidl::android::hardware::wifi::WifiChannelInfo;
using aidl::android::hardware::wifi::WifiChannelWidthInMhz;
@@ -87,6 +91,15 @@
return caps;
}
+ int getMostSignificantSetBitMask(int n) {
+ if (n == 0) return 0;
+ int pos = std::numeric_limits<int>::digits - 1;
+ while ((n & (1 << pos)) == 0) {
+ pos--;
+ }
+ return 1 << pos;
+ }
+
std::shared_ptr<IWifiRttController> wifi_rtt_controller_;
int interface_version_;
@@ -161,6 +174,66 @@
}
/*
+ * Request80211azNtbSecureRangeMeasurement
+ * Tests the two sided 11az non-trigger based secure ranging - 802.11az NTB FTM protocol.
+ */
+TEST_P(WifiRttControllerAidlTest, Request80211azNtbSecureRangeMeasurement) {
+ if (interface_version_ < 3) {
+ GTEST_SKIP() << "Request80211azNtbRangeMeasurement is available as of RttController V3";
+ }
+
+ RttCapabilities caps = getCapabilities();
+ if (!caps.ntbInitiatorSupported) {
+ GTEST_SKIP() << "Skipping 11az NTB RTT since driver/fw does not support";
+ }
+ if (!caps.secureHeLtfSupported && !caps.rangingFrameProtectionSupported) {
+ GTEST_SKIP() << "Skipping 11az NTB secure RTT since driver/fw does not support";
+ }
+ if (!(caps.akmsSupported & Akm::PASN)) {
+ GTEST_SKIP() << "Skipping 11az NTB secure RTT since driver/fw does not support PASN";
+ }
+ if (!caps.cipherSuitesSupported) {
+ GTEST_SKIP()
+ << "Skipping 11az NTB secure RTT since driver/fw does not support Cipher Suites";
+ }
+
+ RttConfig config;
+ config.addr = {{0x00, 0x01, 0x02, 0x03, 0x04, 0x05}};
+ config.type = RttType::TWO_SIDED_11AZ_NTB_SECURE;
+ config.peer = RttPeerType::AP;
+ config.channel.width = WifiChannelWidthInMhz::WIDTH_80;
+ config.channel.centerFreq = 5180;
+ config.channel.centerFreq0 = 5210;
+ config.channel.centerFreq1 = 0;
+ config.bw = RttBw::BW_20MHZ;
+ config.preamble = RttPreamble::HT;
+ config.mustRequestLci = false;
+ config.mustRequestLcr = false;
+ config.numFramesPerBurst = 8;
+ config.numRetriesPerRttFrame = 0;
+ config.numRetriesPerFtmr = 0;
+ // 11az non-trigger based minimum measurement time in units of 100 microseconds.
+ config.ntbMinMeasurementTime = 2500;
+ // 11az non-trigger based maximum measurement time in units of 10 milliseconds.
+ config.ntbMaxMeasurementTime = 1500;
+ RttSecureConfig secureConfig;
+ // PASN is a must to test secure config; which does not need any password.
+ secureConfig.pasnConfig.baseAkm = Akm::PASN;
+ // Get the best Cipher suite supported by the chip.
+ secureConfig.pasnConfig.cipherSuite = getMostSignificantSetBitMask(caps.cipherSuitesSupported);
+ secureConfig.enableSecureHeLtf = caps.secureHeLtfSupported;
+ secureConfig.enableRangingFrameProtection = caps.rangingFrameProtectionSupported;
+ config.secureConfig = secureConfig;
+
+ int cmdId = 55;
+ std::vector<RttConfig> configs = {config};
+ EXPECT_TRUE(wifi_rtt_controller_->rangeRequest(cmdId, configs).isOk());
+
+ // Sleep for 2 seconds to wait for driver/firmware to complete RTT.
+ sleep(2);
+}
+
+/*
* Request80211azNtbRangeMeasurement
* Tests the two sided 11az non-trigger based ranging - 802.11az NTB FTM protocol.
*/
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
index 384ac9a..b0141df 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
@@ -84,8 +84,8 @@
oneway void onPmkSaCacheAdded(in android.hardware.wifi.supplicant.PmkSaCacheData pmkSaData);
oneway void onUsdPublishStarted(in int cmdId, in int publishId);
oneway void onUsdSubscribeStarted(in int cmdId, in int subscribeId);
- oneway void onUsdPublishConfigFailed(in int cmdId);
- oneway void onUsdSubscribeConfigFailed(in int cmdId);
+ oneway void onUsdPublishConfigFailed(in int cmdId, in android.hardware.wifi.supplicant.ISupplicantStaIfaceCallback.UsdConfigErrorCode errorCode);
+ oneway void onUsdSubscribeConfigFailed(in int cmdId, in android.hardware.wifi.supplicant.ISupplicantStaIfaceCallback.UsdConfigErrorCode errorCode);
oneway void onUsdPublishTerminated(in int publishId, in android.hardware.wifi.supplicant.UsdTerminateReasonCode reasonCode);
oneway void onUsdSubscribeTerminated(in int subscribeId, in android.hardware.wifi.supplicant.UsdTerminateReasonCode reasonCode);
oneway void onUsdPublishReplied(in android.hardware.wifi.supplicant.UsdServiceDiscoveryInfo info);
@@ -97,4 +97,10 @@
MULTI_LINK_RECONFIG_AP_REMOVAL = 1,
MULTI_LINK_DYNAMIC_RECONFIG = 2,
}
+ @Backing(type="int") @VintfStability
+ enum UsdConfigErrorCode {
+ FAILURE_UNKNOWN = 0,
+ FAILURE_TIMEOUT = 1,
+ FAILURE_NOT_AVAILABLE = 2,
+ }
}
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
index 1ee873a..efbd066 100644
--- a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
@@ -439,12 +439,32 @@
void onUsdSubscribeStarted(in int cmdId, in int subscribeId);
/**
+ * Error codes returned by |onUsdPublishConfigFailed| and |onUsdSubscribeConfigFailed|.
+ */
+ @VintfStability
+ @Backing(type="int")
+ enum UsdConfigErrorCode {
+ /**
+ * Unknown failure.
+ */
+ FAILURE_UNKNOWN = 0,
+ /**
+ * The requested operation timed out.
+ */
+ FAILURE_TIMEOUT = 1,
+ /**
+ * The requested operation is currently not available.
+ */
+ FAILURE_NOT_AVAILABLE = 2,
+ }
+
+ /**
* Called in response to |ISupplicantStaIface.startUsdPublish| to indicate that the
* publish session could not be configured.
*
* @param cmdId Identifier for the original request.
*/
- void onUsdPublishConfigFailed(in int cmdId);
+ void onUsdPublishConfigFailed(in int cmdId, in UsdConfigErrorCode errorCode);
/**
* Called in response to |ISupplicantStaIface.startUsdSubscribe| to indicate that the
@@ -452,7 +472,7 @@
*
* @param cmdId Identifier for the original request.
*/
- void onUsdSubscribeConfigFailed(in int cmdId);
+ void onUsdSubscribeConfigFailed(in int cmdId, in UsdConfigErrorCode errorCode);
/**
* Called in response to |ISupplicantStaIface.cancelUsdPublish| to indicate that the session
diff --git a/wifi/supplicant/aidl/vts/functional/Android.bp b/wifi/supplicant/aidl/vts/functional/Android.bp
index f94eb46..95ff6cd 100644
--- a/wifi/supplicant/aidl/vts/functional/Android.bp
+++ b/wifi/supplicant/aidl/vts/functional/Android.bp
@@ -137,3 +137,41 @@
"vts",
],
}
+
+cc_test {
+ name: "VtsHalWifiSupplicantP2pNetworkTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: ["supplicant_p2p_network_aidl_test.cpp"],
+ shared_libs: [
+ "libbinder",
+ "libbinder_ndk",
+ "libvndksupport",
+ ],
+ static_libs: [
+ "android.hardware.wifi@1.0",
+ "android.hardware.wifi@1.1",
+ "android.hardware.wifi@1.2",
+ "android.hardware.wifi@1.3",
+ "android.hardware.wifi@1.4",
+ "android.hardware.wifi@1.5",
+ "android.hardware.wifi.common-V2-ndk",
+ "android.hardware.wifi.supplicant@1.0",
+ "android.hardware.wifi.supplicant@1.1",
+ "android.hardware.wifi.supplicant-V4-ndk",
+ "libwifi-system",
+ "libwifi-system-iface",
+ "VtsHalWifiV1_0TargetTestUtil",
+ "VtsHalWifiV1_5TargetTestUtil",
+ "VtsHalWifiSupplicantV1_0TargetTestUtil",
+ "android.hardware.wifi.common-V2-ndk",
+ "android.hardware.wifi-V3-ndk",
+ "VtsHalWifiTargetTestUtil",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp b/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp
index 3638ac5..778e20a 100644
--- a/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp
+++ b/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp
@@ -38,6 +38,7 @@
using aidl::android::hardware::wifi::supplicant::IfaceType;
using aidl::android::hardware::wifi::supplicant::ISupplicant;
using aidl::android::hardware::wifi::supplicant::ISupplicantP2pIface;
+using aidl::android::hardware::wifi::supplicant::ISupplicantP2pNetwork;
using aidl::android::hardware::wifi::supplicant::MiracastMode;
using aidl::android::hardware::wifi::supplicant::P2pAddGroupConfigurationParams;
using aidl::android::hardware::wifi::supplicant::P2pConnectInfo;
@@ -398,8 +399,8 @@
* SetWpsModelNumber
*/
TEST_P(SupplicantP2pIfaceAidlTest, SetWpsModelNumber) {
- const std::string modelNumber = "TestModelNumber";
- EXPECT_TRUE(p2p_iface_->setWpsModelName(modelNumber).isOk());
+ const std::string modelNumber = "Model1234";
+ EXPECT_TRUE(p2p_iface_->setWpsModelNumber(modelNumber).isOk());
}
/*
@@ -981,6 +982,96 @@
EXPECT_TRUE(p2p_iface_->reinvokePersistentGroup(params).isOk());
}
+/*
+ * Test the P2P network management functions.
+ */
+TEST_P(SupplicantP2pIfaceAidlTest, ManageNetworks) {
+ std::shared_ptr<ISupplicantP2pNetwork> network;
+ EXPECT_TRUE(p2p_iface_->addNetwork(&network).isOk());
+ ASSERT_NE(network, nullptr);
+
+ std::vector<int32_t> networkList;
+ EXPECT_TRUE(p2p_iface_->listNetworks(&networkList).isOk());
+ ASSERT_FALSE(networkList.empty());
+
+ int networkId = networkList[0];
+ EXPECT_TRUE(p2p_iface_->getNetwork(networkId, &network).isOk());
+ ASSERT_NE(network, nullptr);
+ EXPECT_TRUE(p2p_iface_->removeNetwork(networkId).isOk());
+}
+
+/*
+ * Request and cancel service discovery
+ */
+TEST_P(SupplicantP2pIfaceAidlTest, RequestAndCancelServiceDiscovery) {
+ int64_t discoveryId;
+ std::vector<uint8_t> query = {0x11, 0x22, 0x33};
+ EXPECT_TRUE(p2p_iface_->requestServiceDiscovery(kTestMacAddr, query, &discoveryId).isOk());
+ EXPECT_TRUE(p2p_iface_->cancelServiceDiscovery(discoveryId).isOk());
+}
+
+/*
+ * Start and stop WPS
+ */
+TEST_P(SupplicantP2pIfaceAidlTest, StartAndStopWps) {
+ // Expected to fail with test values
+ std::string generatedPin;
+ EXPECT_FALSE(p2p_iface_->startWpsPbc(kTestGroupIfName, kTestMacAddr).isOk());
+ EXPECT_FALSE(
+ p2p_iface_->startWpsPinDisplay(kTestGroupIfName, kTestMacAddr, &generatedPin).isOk());
+ EXPECT_FALSE(p2p_iface_->startWpsPinKeypad(kTestGroupIfName, kTestConnectPin).isOk());
+ EXPECT_FALSE(p2p_iface_->cancelWps(kTestGroupIfName).isOk());
+}
+
+/*
+ * Create message and report handover for NFC Request
+ */
+TEST_P(SupplicantP2pIfaceAidlTest, CreateAndReportNfcRequest) {
+ std::vector<uint8_t> requestMsg;
+ EXPECT_TRUE(p2p_iface_->createNfcHandoverRequestMessage(&requestMsg).isOk());
+ EXPECT_FALSE(requestMsg.empty());
+ EXPECT_TRUE(p2p_iface_->reportNfcHandoverResponse(requestMsg).isOk());
+}
+
+/*
+ * Create message and report handover for NFC Select
+ */
+TEST_P(SupplicantP2pIfaceAidlTest, CreateAndReportNfcSelect) {
+ std::vector<uint8_t> selectMsg;
+ EXPECT_TRUE(p2p_iface_->createNfcHandoverSelectMessage(&selectMsg).isOk());
+ EXPECT_FALSE(selectMsg.empty());
+ EXPECT_TRUE(p2p_iface_->reportNfcHandoverInitiation(selectMsg).isOk());
+}
+
+/*
+ * RemoveClient
+ */
+TEST_P(SupplicantP2pIfaceAidlTest, RemoveClient) {
+ // Method returns success for any valid MAC address
+ EXPECT_TRUE(p2p_iface_->removeClient(kTestMacAddr, false).isOk());
+ // Returns failure for any invalid MAC address
+ std::vector<uint8_t> invalidMacAddr = {0x11, 0x22};
+ EXPECT_FALSE(p2p_iface_->removeClient(invalidMacAddr, false).isOk());
+}
+
+/*
+ * ConfigureEapolIpAddressAllocationParams
+ */
+TEST_P(SupplicantP2pIfaceAidlTest, ConfigureEapolIpAddressAllocationParams) {
+ if (interface_version_ < 2) {
+ GTEST_SKIP() << "ConfigureEapolIpAddressAllocationParams is available as of Supplicant V2";
+ }
+ // The IP addresses are IPV4 addresses and higher-order address bytes are in the
+ // lower-order int bytes (e.g. 192.168.1.1 is represented as 0x0101A8C0)
+ EXPECT_TRUE(p2p_iface_
+ ->configureEapolIpAddressAllocationParams(0x0101A8C0, 0x00FFFFFF,
+ 0x0501A8C0, 0x0801A8C0)
+ .isOk());
+
+ // Clear the configuration.
+ EXPECT_TRUE(p2p_iface_->configureEapolIpAddressAllocationParams(0, 0, 0, 0).isOk());
+}
+
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantP2pIfaceAidlTest);
INSTANTIATE_TEST_SUITE_P(Supplicant, SupplicantP2pIfaceAidlTest,
testing::ValuesIn(android::getAidlHalInstanceNames(
diff --git a/wifi/supplicant/aidl/vts/functional/supplicant_p2p_network_aidl_test.cpp b/wifi/supplicant/aidl/vts/functional/supplicant_p2p_network_aidl_test.cpp
new file mode 100644
index 0000000..c5a73f1
--- /dev/null
+++ b/wifi/supplicant/aidl/vts/functional/supplicant_p2p_network_aidl_test.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2025 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 <VtsCoreUtil.h>
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/wifi/supplicant/BnSupplicant.h>
+#include <android/binder_manager.h>
+#include <android/binder_status.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cutils/properties.h>
+
+#include "supplicant_test_utils.h"
+#include "wifi_aidl_test_utils.h"
+
+using aidl::android::hardware::wifi::supplicant::DebugLevel;
+using aidl::android::hardware::wifi::supplicant::IfaceType;
+using aidl::android::hardware::wifi::supplicant::ISupplicantP2pNetwork;
+using aidl::android::hardware::wifi::supplicant::MacAddress;
+using android::ProcessState;
+
+class SupplicantP2pNetworkAidlTest : public testing::TestWithParam<std::string> {
+ public:
+ void SetUp() override {
+ initializeService();
+ supplicant_ = getSupplicant(GetParam().c_str());
+ ASSERT_NE(supplicant_, nullptr);
+ ASSERT_TRUE(supplicant_->setDebugParams(DebugLevel::EXCESSIVE, true, true).isOk());
+
+ bool p2pEnabled = testing::deviceSupportsFeature("android.hardware.wifi.direct");
+ if (!p2pEnabled) {
+ GTEST_SKIP() << "Wi-Fi Direct is not supported, skip this test.";
+ }
+
+ EXPECT_TRUE(supplicant_->getP2pInterface(getP2pIfaceName(), &p2p_iface_).isOk());
+ ASSERT_NE(p2p_iface_, nullptr);
+ EXPECT_TRUE(p2p_iface_->addNetwork(&p2p_network_).isOk());
+ ASSERT_NE(p2p_network_, nullptr);
+ }
+
+ void TearDown() override {
+ stopSupplicantService();
+ startWifiFramework();
+ }
+
+ protected:
+ std::shared_ptr<ISupplicant> supplicant_;
+ std::shared_ptr<ISupplicantP2pIface> p2p_iface_;
+ std::shared_ptr<ISupplicantP2pNetwork> p2p_network_;
+};
+
+/*
+ * GetBssid
+ */
+TEST_P(SupplicantP2pNetworkAidlTest, GetBssid) {
+ std::vector<uint8_t> bssid;
+ EXPECT_TRUE(p2p_network_->getBssid(&bssid).isOk());
+}
+
+/*
+ * GetClientList
+ */
+TEST_P(SupplicantP2pNetworkAidlTest, GetClientList) {
+ // Expect failure if there are no clients
+ std::vector<MacAddress> clientList;
+ EXPECT_FALSE(p2p_network_->getClientList(&clientList).isOk());
+}
+
+/*
+ * GetId
+ */
+TEST_P(SupplicantP2pNetworkAidlTest, GetId) {
+ int networkId;
+ EXPECT_TRUE(p2p_network_->getId(&networkId).isOk());
+}
+
+/*
+ * GetInterfaceName
+ */
+TEST_P(SupplicantP2pNetworkAidlTest, GetInterfaceName) {
+ std::string expectedName = getP2pIfaceName();
+ std::string retrievedName;
+ EXPECT_TRUE(p2p_network_->getInterfaceName(&retrievedName).isOk());
+ EXPECT_EQ(retrievedName, expectedName);
+}
+
+/*
+ * GetSsid
+ */
+TEST_P(SupplicantP2pNetworkAidlTest, GetSsid) {
+ std::vector<uint8_t> ssid;
+ EXPECT_TRUE(p2p_network_->getSsid(&ssid).isOk());
+}
+
+/*
+ * GetType
+ */
+TEST_P(SupplicantP2pNetworkAidlTest, GetType) {
+ IfaceType ifaceType;
+ EXPECT_TRUE(p2p_network_->getType(&ifaceType).isOk());
+ EXPECT_EQ(ifaceType, IfaceType::P2P);
+}
+
+/*
+ * IsCurrent
+ */
+TEST_P(SupplicantP2pNetworkAidlTest, IsCurrent) {
+ bool isCurrent;
+ EXPECT_TRUE(p2p_network_->isCurrent(&isCurrent).isOk());
+ EXPECT_FALSE(isCurrent);
+}
+
+/*
+ * IsGroupOwner
+ */
+TEST_P(SupplicantP2pNetworkAidlTest, IsGroupOwner) {
+ bool isGroupOwner;
+ EXPECT_TRUE(p2p_network_->isGroupOwner(&isGroupOwner).isOk());
+ EXPECT_FALSE(isGroupOwner);
+}
+
+/*
+ * IsPersistent
+ */
+TEST_P(SupplicantP2pNetworkAidlTest, IsPersistent) {
+ bool isPersistent;
+ EXPECT_TRUE(p2p_network_->isPersistent(&isPersistent).isOk());
+ EXPECT_FALSE(isPersistent);
+}
+
+/*
+ * SetClientList
+ */
+TEST_P(SupplicantP2pNetworkAidlTest, SetClientList) {
+ MacAddress client = {{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc}};
+ std::vector clientList = {client};
+ EXPECT_TRUE(p2p_network_->setClientList(clientList).isOk());
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantP2pNetworkAidlTest);
+INSTANTIATE_TEST_SUITE_P(
+ Supplicant, SupplicantP2pNetworkAidlTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(ISupplicant::descriptor)),
+ android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ProcessState::self()->setThreadPoolMaxThreadCount(1);
+ ProcessState::self()->startThreadPool();
+ return RUN_ALL_TESTS();
+}
diff --git a/wifi/supplicant/aidl/vts/functional/supplicant_sta_iface_aidl_test.cpp b/wifi/supplicant/aidl/vts/functional/supplicant_sta_iface_aidl_test.cpp
index da12a82..3a01c5b 100644
--- a/wifi/supplicant/aidl/vts/functional/supplicant_sta_iface_aidl_test.cpp
+++ b/wifi/supplicant/aidl/vts/functional/supplicant_sta_iface_aidl_test.cpp
@@ -47,8 +47,12 @@
using aidl::android::hardware::wifi::supplicant::QosCharacteristics;
using aidl::android::hardware::wifi::supplicant::QosPolicyScsData;
using aidl::android::hardware::wifi::supplicant::QosPolicyScsRequestStatus;
+using aidl::android::hardware::wifi::supplicant::UsdBaseConfig;
+using aidl::android::hardware::wifi::supplicant::UsdCapabilities;
using aidl::android::hardware::wifi::supplicant::UsdMessageInfo;
+using aidl::android::hardware::wifi::supplicant::UsdPublishConfig;
using aidl::android::hardware::wifi::supplicant::UsdServiceDiscoveryInfo;
+using aidl::android::hardware::wifi::supplicant::UsdSubscribeConfig;
using aidl::android::hardware::wifi::supplicant::UsdTerminateReasonCode;
using aidl::android::hardware::wifi::supplicant::WpaDriverCapabilitiesMask;
using aidl::android::hardware::wifi::supplicant::WpsConfigMethods;
@@ -254,10 +258,12 @@
int32_t /* subscribeId */) override {
return ndk::ScopedAStatus::ok();
}
- ::ndk::ScopedAStatus onUsdPublishConfigFailed(int32_t /* cmdId */) override {
+ ::ndk::ScopedAStatus onUsdPublishConfigFailed(int32_t /* cmdId */,
+ UsdConfigErrorCode /* errorCode */) override {
return ndk::ScopedAStatus::ok();
}
- ::ndk::ScopedAStatus onUsdSubscribeConfigFailed(int32_t /* cmdId */) override {
+ ::ndk::ScopedAStatus onUsdSubscribeConfigFailed(int32_t /* cmdId */,
+ UsdConfigErrorCode /* errorCode */) override {
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus onUsdPublishTerminated(int32_t /* publishId */,
@@ -878,6 +884,83 @@
EXPECT_EQ(1, responseList.size());
}
+/*
+ * Verify that all USD methods check the Service Specific Info (SSI) length
+ * and fail if the provided SSI is too long.
+ */
+TEST_P(SupplicantStaIfaceAidlTest, InvalidUsdServiceSpecificInfo) {
+ if (interface_version_ < 4) {
+ GTEST_SKIP() << "USD is available as of Supplicant V4";
+ }
+
+ UsdCapabilities caps;
+ EXPECT_TRUE(sta_iface_->getUsdCapabilities(&caps).isOk());
+ if (!caps.isUsdPublisherSupported && !caps.isUsdSubscriberSupported) {
+ GTEST_SKIP() << "USD publish and subscribe are not supported";
+ }
+
+ int commandId = 123;
+ std::vector<uint8_t> invalidSsi(caps.maxLocalSsiLengthBytes + 1);
+ UsdBaseConfig invalidBaseConfig;
+ invalidBaseConfig.serviceSpecificInfo = invalidSsi;
+
+ if (caps.isUsdPublisherSupported) {
+ UsdPublishConfig publishConfig;
+ publishConfig.usdBaseConfig = invalidBaseConfig;
+ EXPECT_FALSE(sta_iface_->startUsdPublish(commandId, publishConfig).isOk());
+ EXPECT_FALSE(sta_iface_->updateUsdPublish(commandId, invalidSsi).isOk());
+ }
+
+ if (caps.isUsdSubscriberSupported) {
+ UsdSubscribeConfig subscribeConfig;
+ subscribeConfig.usdBaseConfig = invalidBaseConfig;
+ EXPECT_FALSE(sta_iface_->startUsdSubscribe(commandId, subscribeConfig).isOk());
+ }
+
+ UsdMessageInfo messageInfo;
+ messageInfo.message = invalidSsi;
+ EXPECT_FALSE(sta_iface_->sendUsdMessage(messageInfo).isOk());
+}
+
+/*
+ * Cancel a USD Publish and Subscribe session.
+ */
+TEST_P(SupplicantStaIfaceAidlTest, CancelUsdSession) {
+ if (interface_version_ < 4) {
+ GTEST_SKIP() << "USD is available as of Supplicant V4";
+ }
+
+ UsdCapabilities caps;
+ EXPECT_TRUE(sta_iface_->getUsdCapabilities(&caps).isOk());
+ if (!caps.isUsdPublisherSupported && !caps.isUsdSubscriberSupported) {
+ GTEST_SKIP() << "USD publish and subscribe are not supported";
+ }
+
+ int sessionId = 123;
+ if (caps.isUsdPublisherSupported) {
+ // Method is expected to succeed, even if the session does not exist.
+ EXPECT_TRUE(sta_iface_->cancelUsdPublish(sessionId).isOk());
+ }
+ if (caps.isUsdSubscriberSupported) {
+ EXPECT_TRUE(sta_iface_->cancelUsdSubscribe(sessionId).isOk());
+ }
+}
+
+/*
+ * GenerateSelfDppConfiguration
+ */
+TEST_P(SupplicantStaIfaceAidlTest, GenerateSelfDppConfiguration) {
+ if (!keyMgmtSupported(sta_iface_, KeyMgmtMask::DPP)) {
+ GTEST_SKIP() << "Missing DPP support";
+ }
+ const std::string ssid = "my_test_ssid";
+ const std::vector<uint8_t> eckey_in = {0x2, 0x3, 0x4};
+
+ // Expect to fail as this test requires a DPP AKM supported AP and a valid private EC
+ // key generated by wpa_supplicant.
+ EXPECT_FALSE(sta_iface_->generateSelfDppConfiguration(ssid, eckey_in).isOk());
+}
+
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantStaIfaceAidlTest);
INSTANTIATE_TEST_SUITE_P(Supplicant, SupplicantStaIfaceAidlTest,
testing::ValuesIn(android::getAidlHalInstanceNames(
diff --git a/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp b/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp
index 9bdd2f5..5e6069f 100644
--- a/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp
+++ b/wifi/supplicant/aidl/vts/functional/supplicant_sta_network_aidl_test.cpp
@@ -32,6 +32,7 @@
using aidl::android::hardware::wifi::supplicant::AuthAlgMask;
using aidl::android::hardware::wifi::supplicant::BnSupplicantStaNetworkCallback;
using aidl::android::hardware::wifi::supplicant::DebugLevel;
+using aidl::android::hardware::wifi::supplicant::DppConnectionKeys;
using aidl::android::hardware::wifi::supplicant::EapMethod;
using aidl::android::hardware::wifi::supplicant::EapPhase2Method;
using aidl::android::hardware::wifi::supplicant::GroupCipherMask;
@@ -837,6 +838,21 @@
}
/*
+ * SetDppKeys
+ */
+TEST_P(SupplicantStaNetworkAidlTest, SetDppKeys) {
+ if (!keyMgmtSupported(sta_iface_, KeyMgmtMask::DPP)) {
+ GTEST_SKIP() << "Missing DPP support";
+ }
+
+ DppConnectionKeys in_keys;
+ in_keys.connector = std::vector<uint8_t>({0x11, 0x22, 0x33, 0x44});
+ in_keys.cSign = std::vector<uint8_t>({0x55, 0x66, 0x77, 0x88});
+ in_keys.netAccessKey = std::vector<uint8_t>({0xaa, 0xbb, 0xcc, 0xdd});
+ EXPECT_TRUE(sta_network_->setDppKeys(in_keys).isOk());
+}
+
+/*
* SetVendorData
*/
TEST_P(SupplicantStaNetworkAidlTest, SetVendorData) {