Merge "camera/device/3.2: Add custom vendor operating modes"
diff --git a/audio/2.0/default/StreamOut.cpp b/audio/2.0/default/StreamOut.cpp
index cdc8ded..6ccdbcd 100644
--- a/audio/2.0/default/StreamOut.cpp
+++ b/audio/2.0/default/StreamOut.cpp
@@ -79,10 +79,6 @@
ssize_t writeResult = mStream->write(mStream, &mBuffer[0], availToRead);
if (writeResult >= 0) {
mStatus.reply.written = writeResult;
- // Diagnostics of the cause of b/35813113.
- ALOGE_IF(writeResult > availToRead,
- "legacy hal reports more bytes written than asked for: %lld > %lld",
- (long long)writeResult, (long long)availToRead);
} else {
mStatus.retval = Stream::analyzeStatus("write", writeResult);
}
diff --git a/audio/2.0/vts/functional/Android.bp b/audio/2.0/vts/functional/Android.bp
new file mode 100644
index 0000000..05b1817
--- /dev/null
+++ b/audio/2.0/vts/functional/Android.bp
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2017 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.
+//
+
+cc_test {
+ name: "VtsHalAudioV2_0TargetTest",
+ gtest: true,
+ srcs: ["AudioPrimaryHidlHalTest.cpp"],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libhidlbase",
+ "libutils",
+ "libcutils",
+ "android.hardware.audio@2.0",
+ "android.hardware.audio.common@2.0",
+ ],
+ static_libs: ["libgtest"],
+ cflags: [
+ "-O0",
+ "-g",
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+}
diff --git a/audio/2.0/vts/functional/AudioPrimaryHidlHalTest.cpp b/audio/2.0/vts/functional/AudioPrimaryHidlHalTest.cpp
new file mode 100644
index 0000000..cc20456
--- /dev/null
+++ b/audio/2.0/vts/functional/AudioPrimaryHidlHalTest.cpp
@@ -0,0 +1,708 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "VtsHalAudioV2_0TargetTest"
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <cstdio>
+#include <limits>
+#include <list>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/logging.h>
+
+#include <android/hardware/audio/2.0/IDevice.h>
+#include <android/hardware/audio/2.0/IDevicesFactory.h>
+#include <android/hardware/audio/2.0/types.h>
+#include <android/hardware/audio/common/2.0/types.h>
+
+#include "utility/ReturnIn.h"
+#include "utility/AssertOk.h"
+#include "utility/PrettyPrintAudioTypes.h"
+
+using std::string;
+using std::to_string;
+using std::vector;
+
+using ::android::sp;
+using ::android::hardware::Return;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::audio::V2_0::DeviceAddress;
+using ::android::hardware::audio::V2_0::IDevice;
+using ::android::hardware::audio::V2_0::IDevicesFactory;
+using ::android::hardware::audio::V2_0::IStream;
+using ::android::hardware::audio::V2_0::IStreamIn;
+using ::android::hardware::audio::V2_0::IStreamOut;
+using ::android::hardware::audio::V2_0::ParameterValue;
+using ::android::hardware::audio::V2_0::Result;
+using ::android::hardware::audio::common::V2_0::AudioChannelMask;
+using ::android::hardware::audio::common::V2_0::AudioConfig;
+using ::android::hardware::audio::common::V2_0::AudioDevice;
+using ::android::hardware::audio::common::V2_0::AudioFormat;
+using ::android::hardware::audio::common::V2_0::AudioHandleConsts;
+using ::android::hardware::audio::common::V2_0::AudioInputFlag;
+using ::android::hardware::audio::common::V2_0::AudioIoHandle;
+using ::android::hardware::audio::common::V2_0::AudioOffloadInfo;
+using ::android::hardware::audio::common::V2_0::AudioOutputFlag;
+using ::android::hardware::audio::common::V2_0::AudioSource;
+
+using utility::returnIn;
+
+namespace doc {
+/** Document the current test case.
+ * Eg: calling `doc::test("Dump the state of the hal")` in the "debugDump" test will output:
+ * <testcase name="debugDump" status="run" time="6" classname="AudioPrimaryHidlTest"
+ description="Dump the state of the hal." />
+ * see https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#logging-additional-information
+ */
+void test(const std::string& testCaseDocumentation) {
+ ::testing::Test::RecordProperty("description", testCaseDocumentation);
+}
+
+/** Document why a test was not fully run. Usually due to an optional feature not implemented. */
+void partialTest(const std::string& reason) {
+ ::testing::Test::RecordProperty("partialyRunTest", reason);
+}
+}
+
+// Register callback for static object destruction
+// Avoid destroying static objects after main return.
+// Post main return destruction leads to incorrect gtest timing measurements as well as harder
+// debuging if anything goes wrong during destruction.
+class Environment : public ::testing::Environment {
+public:
+ using TearDownFunc = std::function<void ()>;
+ void registerTearDown(TearDownFunc&& tearDown) {
+ tearDowns.push_back(std::move(tearDown));
+ }
+
+private:
+ void TearDown() override {
+ // Call the tear downs in reverse order of insertion
+ for (auto& tearDown : tearDowns) {
+ tearDown();
+ }
+ }
+ std::list<TearDownFunc> tearDowns;
+};
+// Instance to register global tearDown
+static Environment* environment;
+
+class HidlTest : public ::testing::Test {
+protected:
+ // Convenient member to store results
+ Result res;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+////////////////////// getService audio_devices_factory //////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+// Test all audio devices
+class AudioHidlTest : public HidlTest {
+public:
+ void SetUp() override {
+ ASSERT_NO_FATAL_FAILURE(HidlTest::SetUp()); // setup base
+
+ if (devicesFactory == nullptr) {
+ environment->registerTearDown([]{ devicesFactory.clear(); });
+ devicesFactory = IDevicesFactory::getService();
+ }
+ ASSERT_TRUE(devicesFactory != nullptr);
+ }
+
+protected:
+ // Cache the devicesFactory retrieval to speed up each test by ~0.5s
+ static sp<IDevicesFactory> devicesFactory;
+};
+sp<IDevicesFactory> AudioHidlTest::devicesFactory;
+
+TEST_F(AudioHidlTest, GetAudioDevicesFactoryService) {
+ doc::test("test the getService (called in SetUp)");
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// openDevice primary ///////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+// Test the primary device
+class AudioPrimaryHidlTest : public AudioHidlTest {
+public:
+ /** Primary HAL test are NOT thread safe. */
+ void SetUp() override {
+ ASSERT_NO_FATAL_FAILURE(AudioHidlTest::SetUp()); // setup base
+
+ if (device == nullptr) {
+ environment->registerTearDown([]{ device.clear(); });
+ IDevicesFactory::Result result;
+ ASSERT_OK(devicesFactory->openDevice(IDevicesFactory::Device::PRIMARY,
+ returnIn(result, device)));
+ }
+ ASSERT_TRUE(device != nullptr);
+ }
+
+protected:
+ // Cache the device opening to speed up each test by ~0.5s
+ static sp<IDevice> device;
+};
+sp<IDevice> AudioPrimaryHidlTest::device;
+
+TEST_F(AudioPrimaryHidlTest, OpenPrimaryDevice) {
+ doc::test("Test the openDevice (called in SetUp)");
+}
+
+TEST_F(AudioPrimaryHidlTest, Init) {
+ doc::test("Test that the audio primary hal initialized correctly");
+ ASSERT_OK(device->initCheck());
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////// {set,get}MasterVolume ///////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+class MasterVolumeTest : public AudioPrimaryHidlTest {
+protected:
+ void testSetGetConsistency(float volume, Result expectedSetResult, float expectedGetVolume) {
+ SCOPED_TRACE("Test set/get consistency for " + to_string(volume));
+ auto ret = device->setMasterVolume(volume);
+ ASSERT_TRUE(ret.isOk());
+ ASSERT_EQ(expectedSetResult, ret);
+
+ float observedVolume;
+ ASSERT_OK(device->getMasterVolume(returnIn(res, observedVolume)));
+ ASSERT_OK(res);
+
+ // Check that `get` returned the expected value
+ EXPECT_EQ(expectedGetVolume, observedVolume);
+ }
+};
+
+TEST_F(MasterVolumeTest, MasterVolumeTest) {
+ doc::test("Test the master volume if supported");
+ {
+ SCOPED_TRACE("Check for master volume support");
+ auto ret = device->setMasterVolume(1);
+ ASSERT_TRUE(ret.isOk());
+ if (ret == Result::NOT_SUPPORTED) {
+ doc::partialTest("Master volume is not supported");
+ return;
+ }
+ }
+ // NOTE: this code has never been tested on a platform supporting MasterVolume
+ float lastValidVolumeSet;
+ using Volumes = float[];
+ SCOPED_TRACE("As set/get master volume are supported...");
+ {
+ SCOPED_TRACE("...test them with valid values");
+ for (float validVolume : Volumes{0, 0.5, 1}) {
+ ASSERT_NO_FATAL_FAILURE(testSetGetConsistency(validVolume, Result::OK, validVolume));
+ lastValidVolumeSet = validVolume;
+ }
+ }{
+ SCOPED_TRACE("...test them with tricky values");
+ for (float outOfBoundVolume :Volumes{-0.1, 1.1, NAN, INFINITY, -INFINITY,
+ 1 + std::numeric_limits<float>::epsilon()}) {
+ ASSERT_NO_FATAL_FAILURE(testSetGetConsistency(outOfBoundVolume,
+ Result::INVALID_ARGUMENTS,
+ lastValidVolumeSet));
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+////////////////////////// {set,get}{Master,Mic}Mute /////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+class BoolAccessorPrimaryHidlTest : public AudioPrimaryHidlTest {
+protected:
+ template <class Getter, class Setter>
+ void testBoolAccessors(const string& propertyName, const vector<bool>& valuesToTest,
+ Setter setter, Getter getter) {
+ for (bool setState : valuesToTest) {
+ SCOPED_TRACE("Test " + propertyName + " state: " + to_string(setState));
+ ASSERT_OK((device.get()->*setter)(setState));
+ bool getState;
+ ASSERT_OK((device.get()->*getter)(returnIn(res, getState)));
+ ASSERT_OK(res);
+ ASSERT_EQ(setState, getState);
+ }
+ }
+};
+
+TEST_F(BoolAccessorPrimaryHidlTest, MicMuteTest) {
+ doc::test("Check that the mic can be muted and unmuted");
+ testBoolAccessors("mic mute", {true, false, true}, &IDevice::setMicMute, &IDevice::getMicMute);
+ // TODO: check that the mic is really muted (all sample are 0)
+}
+
+TEST_F(BoolAccessorPrimaryHidlTest, MasterMuteTest) {
+ doc::test("If master mute is supported, try to mute and unmute the master output");
+ {
+ SCOPED_TRACE("Check for master mute support");
+ auto ret = device->setMasterMute(false);
+ ASSERT_TRUE(ret.isOk());
+ if (ret == Result::NOT_SUPPORTED) {
+ doc::partialTest("Master mute is not supported");
+ return;
+ }
+ }
+ // NOTE: this code has never been tested on a platform supporting MasterMute
+ testBoolAccessors("master mute", {true, false, true},
+ &IDevice::setMasterMute, &IDevice::getMasterMute);
+ // TODO: check that the master volume is really muted
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////// Required and recommended audio format support ///////////////
+// From: https://source.android.com/compatibility/android-cdd.html#5_4_audio_recording
+// From: https://source.android.com/compatibility/android-cdd.html#5_5_audio_playback
+/////////// TODO: move to the beginning of the file for easier update ////////
+//////////////////////////////////////////////////////////////////////////////
+
+class AudioConfigPrimaryTest : public AudioPrimaryHidlTest {
+public:
+ // Cache result ?
+ static const vector<AudioConfig> getRequiredSupportPlaybackAudioConfig() {
+ return combineAudioConfig({AudioChannelMask::OUT_STEREO, AudioChannelMask::OUT_MONO},
+ {8000, 11025, 16000, 22050, 32000, 44100},
+ {AudioFormat::PCM_16_BIT});
+ }
+
+ static const vector<AudioConfig> getRecommendedSupportPlaybackAudioConfig() {
+ return combineAudioConfig({AudioChannelMask::OUT_STEREO, AudioChannelMask::OUT_MONO},
+ {24000, 48000},
+ {AudioFormat::PCM_16_BIT});
+ }
+
+ static const vector<AudioConfig> getSupportedPlaybackAudioConfig() {
+ // TODO: retrieve audio config supported by the platform
+ // as declared in the policy configuration
+ return {};
+ }
+
+ static const vector<AudioConfig> getRequiredSupportCaptureAudioConfig() {
+ return combineAudioConfig({AudioChannelMask::IN_MONO},
+ {8000, 11025, 16000, 44100},
+ {AudioFormat::PCM_16_BIT});
+ }
+ static const vector<AudioConfig> getRecommendedSupportCaptureAudioConfig() {
+ return combineAudioConfig({AudioChannelMask::IN_STEREO},
+ {22050, 48000},
+ {AudioFormat::PCM_16_BIT});
+ }
+ static const vector<AudioConfig> getSupportedCaptureAudioConfig() {
+ // TODO: retrieve audio config supported by the platform
+ // as declared in the policy configuration
+ return {};
+ }
+private:
+ static const vector<AudioConfig> combineAudioConfig(
+ vector<AudioChannelMask> channelMasks,
+ vector<uint32_t> sampleRates,
+ vector<AudioFormat> formats) {
+ vector<AudioConfig> configs;
+ for (auto channelMask: channelMasks) {
+ for (auto sampleRate : sampleRates) {
+ for (auto format : formats) {
+ AudioConfig config{};
+ // leave offloadInfo to 0
+ config.channelMask = channelMask;
+ config.sampleRateHz = sampleRate;
+ config.format = format;
+ // FIXME: leave frameCount to 0 ?
+ configs.push_back(config);
+ }
+ }
+ }
+ return configs;
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+///////////////////////////// getInputBufferSize /////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+// FIXME: execute input test only if platform declares android.hardware.microphone
+// how to get this value ? is it a property ???
+
+class AudioCaptureConfigPrimaryTest : public AudioConfigPrimaryTest,
+ public ::testing::WithParamInterface<AudioConfig> {
+protected:
+ void inputBufferSizeTest(const AudioConfig& audioConfig, bool supportRequired) {
+ uint64_t bufferSize;
+ ASSERT_OK(device->getInputBufferSize(audioConfig, returnIn(res, bufferSize)));
+
+ switch (res) {
+ case Result::INVALID_ARGUMENTS:
+ EXPECT_FALSE(supportRequired);
+ break;
+ case Result::OK:
+ // Check that the buffer is of a sane size
+ // For now only that it is > 0
+ EXPECT_GT(bufferSize, uint64_t(0));
+ break;
+ default:
+ FAIL() << "Invalid return status: " << ::testing::PrintToString(res);
+ }
+ }
+};
+
+// Test that the required capture config and those declared in the policy are indeed supported
+class RequiredInputBufferSizeTest : public AudioCaptureConfigPrimaryTest {};
+TEST_P(RequiredInputBufferSizeTest, RequiredInputBufferSizeTest) {
+ doc::test("Input buffer size must be retrievable for a format with required support.");
+ inputBufferSizeTest(GetParam(), true);
+}
+INSTANTIATE_TEST_CASE_P(
+ RequiredInputBufferSize, RequiredInputBufferSizeTest,
+ ::testing::ValuesIn(AudioConfigPrimaryTest::getRequiredSupportCaptureAudioConfig()));
+INSTANTIATE_TEST_CASE_P(
+ SupportedInputBufferSize, RequiredInputBufferSizeTest,
+ ::testing::ValuesIn(AudioConfigPrimaryTest::getSupportedCaptureAudioConfig()));
+
+// Test that the recommended capture config are supported or lead to a INVALID_ARGUMENTS return
+class OptionalInputBufferSizeTest : public AudioCaptureConfigPrimaryTest {};
+TEST_P(OptionalInputBufferSizeTest, OptionalInputBufferSizeTest) {
+ doc::test("Input buffer size should be retrievable for a format with recommended support.");
+ inputBufferSizeTest(GetParam(), false);
+}
+INSTANTIATE_TEST_CASE_P(
+ RecommendedCaptureAudioConfigSupport, OptionalInputBufferSizeTest,
+ ::testing::ValuesIn(AudioConfigPrimaryTest::getRecommendedSupportCaptureAudioConfig()));
+
+//////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// setScreenState ///////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+TEST_F(AudioPrimaryHidlTest, setScreenState) {
+ doc::test("Check that the hal can receive the screen state");
+ for (bool turnedOn : {false, true, true, false, false}) {
+ auto ret = device->setScreenState(turnedOn);
+ ASSERT_TRUE(ret.isOk());
+ Result result = ret;
+ ASSERT_TRUE(result == Result::OK || result == Result::NOT_SUPPORTED) << toString(result);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////// {get,set}Parameters /////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+TEST_F(AudioPrimaryHidlTest, getParameters) {
+ doc::test("Check that the hal can set and get parameters");
+ hidl_vec<hidl_string> keys;
+ hidl_vec<ParameterValue> values;
+ ASSERT_OK(device->getParameters(keys, returnIn(res, values)));
+ ASSERT_OK(device->setParameters(values));
+ values.resize(0);
+ ASSERT_OK(device->setParameters(values));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////// debugDebug //////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+TEST_F(AudioPrimaryHidlTest, debugDump) {
+ doc::test("Check that the hal can dump its state without error");
+ FILE* file = tmpfile();
+ ASSERT_NE(nullptr, file) << errno;
+
+ auto* nativeHandle = native_handle_create(1, 0);
+ ASSERT_NE(nullptr, nativeHandle);
+ nativeHandle->data[0] = fileno(file);
+
+ hidl_handle handle;
+ handle.setTo(nativeHandle, true /*take ownership*/);
+
+ auto ret = device->debugDump(handle);
+ ASSERT_TRUE(ret.isOk());
+
+ // FIXME: debugDump does not return a Result.
+ // This mean that the hal can not report that it not implementing the function.
+ // As the default hal does not implement debugDump, the function can not be tested.
+ /*
+ res = ret;
+ if (res == Result::NOT_SUPPORTED) {
+ doc::partialTest("debugDump is not implemented");
+ return;
+ }
+ ASSERT_OK(res);
+ rewind(file);
+
+ // Check that at least one bit was written by the hal
+ char buff;
+ ASSERT_EQ(1, fread(&buff, sizeof(buff), 1, file));
+ */
+ EXPECT_EQ(0, fclose(file)) << errno;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+////////////////////////// open{Output,Input}Stream //////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+template <class Stream>
+class OpenStreamTest : public AudioConfigPrimaryTest,
+ public ::testing::WithParamInterface<AudioConfig> {
+protected:
+ template <class Open>
+ void testOpen(Open openStream, const AudioConfig& config) {
+ // FIXME: Open a stream without an IOHandle
+ // This is not required to be accepted by hal implementations
+ AudioIoHandle ioHandle = (AudioIoHandle)AudioHandleConsts::AUDIO_IO_HANDLE_NONE;
+ AudioConfig suggestedConfig{};
+ ASSERT_OK(openStream(ioHandle, config, returnIn(res, stream, suggestedConfig)));
+
+ // TODO: only allow failure for RecommendedPlaybackAudioConfig
+ switch (res) {
+ case Result::OK:
+ ASSERT_TRUE(stream != nullptr);
+ audioConfig = config;
+ break;
+ case Result::INVALID_ARGUMENTS:
+ ASSERT_TRUE(stream == nullptr);
+ AudioConfig suggestedConfigRetry;
+ // Could not open stream with config, try again with the suggested one
+ ASSERT_OK(openStream(ioHandle, suggestedConfig,
+ returnIn(res, stream, suggestedConfigRetry)));
+ // This time it must succeed
+ ASSERT_OK(res);
+ ASSERT_TRUE(stream == nullptr);
+ audioConfig = suggestedConfig;
+ break;
+ default:
+ FAIL() << "Invalid return status: " << ::testing::PrintToString(res);
+ }
+ open = true;
+ }
+
+private:
+ void TearDown() override {
+ if (open) {
+ ASSERT_OK(stream->close());
+ }
+ }
+
+protected:
+
+ AudioConfig audioConfig;
+ sp<Stream> stream;
+ bool open = false;
+};
+
+////////////////////////////// openOutputStream //////////////////////////////
+
+class OutputStreamTest : public OpenStreamTest<IStreamOut> {
+ virtual void SetUp() override {
+ ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp()); // setup base
+
+ const AudioConfig& config = GetParam();
+ DeviceAddress deviceAddr{}; // Ignored by primary hal
+ AudioOutputFlag flags = AudioOutputFlag::NONE; // TODO: test all flag combination
+ testOpen([&](AudioIoHandle handle, AudioConfig config, auto cb)
+ { return device->openOutputStream(handle, deviceAddr, config, flags, cb); },
+ config);
+ }
+};
+TEST_P(OutputStreamTest, OpenOutputStreamTest) {
+ doc::test("Check that output streams can be open with the required and recommended config");
+ // Open done in SetUp
+}
+INSTANTIATE_TEST_CASE_P(
+ RequiredOutputStreamConfigSupport, OutputStreamTest,
+ ::testing::ValuesIn(AudioConfigPrimaryTest::getRequiredSupportPlaybackAudioConfig()));
+INSTANTIATE_TEST_CASE_P(
+ SupportedOutputStreamConfig, OutputStreamTest,
+ ::testing::ValuesIn(AudioConfigPrimaryTest::getSupportedPlaybackAudioConfig()));
+
+INSTANTIATE_TEST_CASE_P(
+ RecommendedOutputStreamConfigSupport, OutputStreamTest,
+ ::testing::ValuesIn(AudioConfigPrimaryTest::getRecommendedSupportPlaybackAudioConfig()));
+
+////////////////////////////// openInputStream //////////////////////////////
+
+class InputStreamTest : public OpenStreamTest<IStreamIn> {
+
+ virtual void SetUp() override {
+ ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp()); // setup base
+
+ const AudioConfig& config = GetParam();
+ DeviceAddress deviceAddr{}; // TODO: test all devices
+ AudioInputFlag flags = AudioInputFlag::NONE; // TODO: test all flag combination
+ AudioSource source = AudioSource::DEFAULT; // TODO: test all flag combination
+ testOpen([&](AudioIoHandle handle, AudioConfig config, auto cb)
+ { return device->openInputStream(handle, deviceAddr, config, flags, source, cb); },
+ config);
+ }
+};
+
+TEST_P(InputStreamTest, OpenInputStreamTest) {
+ doc::test("Check that input streams can be open with the required and recommended config");
+ // Open done in setup
+}
+INSTANTIATE_TEST_CASE_P(
+ RequiredInputStreamConfigSupport, InputStreamTest,
+ ::testing::ValuesIn(AudioConfigPrimaryTest::getRequiredSupportCaptureAudioConfig()));
+INSTANTIATE_TEST_CASE_P(
+ SupportedInputStreamConfig, InputStreamTest,
+ ::testing::ValuesIn(AudioConfigPrimaryTest::getSupportedCaptureAudioConfig()));
+
+INSTANTIATE_TEST_CASE_P(
+ RecommendedInputStreamConfigSupport, InputStreamTest,
+ ::testing::ValuesIn(AudioConfigPrimaryTest::getRecommendedSupportCaptureAudioConfig()));
+
+//////////////////////////////////////////////////////////////////////////////
+////////////////////////////// IStream getters ///////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+/** Unpack the provided result.
+ * If the result is not OK, register a failure and return an undefined value. */
+template <class R>
+static R extract(Return<R> ret) {
+ if (!ret.isOk()) {
+ ADD_FAILURE();
+ return R{};
+ }
+ return ret;
+}
+
+template <class Property, class CapabilityGetter, class Getter, class Setter>
+static void testCapabilityGetter(const string& name,IStream* stream, Property currentValue,
+ CapabilityGetter capablityGetter, Getter getter, Setter setter) {
+ hidl_vec<Property> capabilities;
+ ASSERT_OK((stream->*capablityGetter)(returnIn(capabilities)));
+ if (capabilities.size() == 0) {
+ // The default hal should probably return a NOT_SUPPORTED if the hal does not expose
+ // capability retrieval. For now it returns an empty list if not implemented
+ doc::partialTest(name + " is not supported");
+ return;
+ };
+ // TODO: This code has never been tested on a hal that supports getSupportedSampleRates
+ EXPECT_NE(std::find(capabilities.begin(), capabilities.end(), currentValue),
+ capabilities.end())
+ << "current " << name << " is not in the list of the supported ones "
+ << toString(capabilities);
+
+ // Check that all declared supported values are indeed supported
+ for (auto capability : capabilities) {
+ ASSERT_OK((stream->*setter)(capability));
+ ASSERT_EQ(capability, extract((stream->*getter)()));
+ }
+}
+
+static void testGetAudioProperties(IStream* stream, AudioConfig expectedConfig) {
+ uint32_t sampleRateHz;
+ AudioChannelMask mask;
+ AudioFormat format;
+
+ stream->getAudioProperties(returnIn(sampleRateHz, mask, format));
+
+ // FIXME: the qcom hal it does not currently negotiate the sampleRate & channel mask
+ // EXPECT_EQ(expectedConfig.sampleRateHz, sampleRateHz);
+ // EXPECT_EQ(expectedConfig.channelMask, mask);
+ EXPECT_EQ(expectedConfig.format, format);
+}
+
+static void testAccessors(IStream* stream, AudioConfig audioConfig) {
+ doc::test("Test IStream getters and setters that can be called in the stop state");
+
+ auto frameCount = extract(stream->getFrameCount());
+ ASSERT_EQ(audioConfig.frameCount, frameCount);
+
+ auto sampleRate = extract(stream->getSampleRate());
+ // FIXME: the qcom hal it does not currently negotiate the sampleRate
+ // ASSERT_EQ(audioConfig.sampleRateHz, sampleRate);
+
+ auto channelMask = extract(stream->getChannelMask());
+ // FIXME: the qcom hal it does not currently negotiate the channelMask
+ // ASSERT_EQ(audioConfig.channelMask, channelMask);
+
+ auto frameSize = extract(stream->getFrameSize());
+ ASSERT_GE(frameSize, 0U);
+
+ auto bufferSize = extract(stream->getBufferSize());
+ ASSERT_GE(bufferSize, frameSize);
+
+ testCapabilityGetter("getSupportedsampleRate", stream, sampleRate,
+ &IStream::getSupportedSampleRates,
+ &IStream::getSampleRate, &IStream::setSampleRate);
+
+ testCapabilityGetter("getSupportedChannelMask", stream, channelMask,
+ &IStream::getSupportedChannelMasks,
+ &IStream::getChannelMask, &IStream::setChannelMask);
+
+ AudioFormat format = extract(stream->getFormat());
+ ASSERT_EQ(audioConfig.format, format);
+
+ testCapabilityGetter("getSupportedFormats", stream, format,
+ &IStream::getSupportedFormats, &IStream::getFormat, &IStream::setFormat);
+
+ testGetAudioProperties(stream, audioConfig);
+
+ // FIXME: Stream wrapper does not implement getDevice properly.
+ // It needs to call getProperty({"routing"}).
+ // The current implementation segfault with the default hal
+ /*
+ * auto ret = stream->getDevice();
+ * ASSERT_TRUE(ret.isOk());
+ * AudioDevice device = ret;
+ * ASSERT_EQ(AudioDevice::OUT_ALL, device);
+ */
+}
+
+TEST_P(InputStreamTest, GettersTest) {
+ testAccessors(stream.get(), audioConfig);
+}
+TEST_P(OutputStreamTest, GettersTest) {
+ testAccessors(stream.get(), audioConfig);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////// AudioPatches ////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+
+TEST_F(AudioPrimaryHidlTest, AudioPatches) {
+ doc::test("Test if audio patches are supported");
+ auto result = device->supportsAudioPatches();
+ ASSERT_TRUE(result.isOk());
+ bool supportsAudioPatch = result;
+ if (!supportsAudioPatch) {
+ doc::partialTest("Audio patches are not supported");
+ return;
+ }
+ // TODO: test audio patches
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////// Clean caches on global tear down ////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+int main(int argc, char** argv) {
+ environment = new Environment;
+ ::testing::AddGlobalTestEnvironment(environment);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+ return status;
+}
diff --git a/audio/2.0/vts/functional/utility/AssertOk.h b/audio/2.0/vts/functional/utility/AssertOk.h
new file mode 100644
index 0000000..5397436
--- /dev/null
+++ b/audio/2.0/vts/functional/utility/AssertOk.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 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 <hidl/Status.h>
+
+namespace detail {
+
+inline void assertOk(::android::hardware::Return<void> ret) {
+ ASSERT_TRUE(ret.isOk());
+}
+
+inline void assertOk(::android::hardware::audio::V2_0::Result result) {
+ ASSERT_EQ(decltype(result)::OK, result);
+}
+
+inline void assertOk(::android::hardware::Return<::android::hardware::audio::V2_0::Result> ret) {
+ ASSERT_TRUE(ret.isOk());
+ ::android::hardware::audio::V2_0::Result result = ret;
+ assertOk(result);
+}
+
+}
+
+// Test anything provided is and contains only OK
+#define ASSERT_OK(ret) ASSERT_NO_FATAL_FAILURE(detail::assertOk(ret))
+#define EXPECT_OK(ret) EXPECT_NO_FATAL_FAILURE(detail::assertOk(ret))
diff --git a/audio/2.0/vts/functional/utility/PrettyPrintAudioTypes.h b/audio/2.0/vts/functional/utility/PrettyPrintAudioTypes.h
new file mode 100644
index 0000000..8dfcb29
--- /dev/null
+++ b/audio/2.0/vts/functional/utility/PrettyPrintAudioTypes.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Use HIDL generated toString methods to pretty print gtest errors
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V2_0 {
+inline void PrintTo(const Result& result, ::std::ostream* os) {
+ *os << toString(result);
+}
+} // namespace V2_0
+namespace common {
+namespace V2_0 {
+inline void PrintTo(const AudioConfig& config, ::std::ostream* os) {
+ *os << toString(config);
+}
+} // namespace V2_0
+} // namespace common
+}
+}
+}
diff --git a/audio/2.0/vts/functional/utility/ReturnIn.h b/audio/2.0/vts/functional/utility/ReturnIn.h
new file mode 100644
index 0000000..bb2389a
--- /dev/null
+++ b/audio/2.0/vts/functional/utility/ReturnIn.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 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 <tuple>
+
+namespace utility {
+
+namespace detail {
+// Helper class to generate the HIDL synchronous callback
+template <class... ResultStore>
+class ReturnIn {
+ public:
+ // Provide to the constructor the variables where the output parameters must be copied
+ // TODO: take pointers to match google output parameter style ?
+ ReturnIn(ResultStore&... ts) : results(ts...) {}
+ // Synchronous callback
+ template <class... Results>
+ void operator() (Results&&...results) {
+ set(std::forward<Results>(results)...);
+ }
+ private:
+ // Recursively set all output parameters
+ template <class Head, class... Tail>
+ void set(Head&& head, Tail&&... tail) {
+ std::get<sizeof...(ResultStore) - sizeof...(Tail) - 1>(results)
+ = std::forward<Head>(head);
+ set(tail...);
+ }
+ // Trivial case
+ void set() {}
+
+ // All variables to set are stored here
+ std::tuple<ResultStore&...> results;
+};
+} // namespace detail
+
+// Generate the HIDL synchronous callback with a copy policy
+// Input: the variables (lvalue reference) where to save the return values
+// Output: the callback to provide to a HIDL call with a synchronous callback
+// The output parameters *will be copied* do not use this function if you have
+// a zero copy policy
+template <class... ResultStore>
+detail::ReturnIn<ResultStore...> returnIn(ResultStore&... ts) { return {ts...};}
+
+}
diff --git a/audio/Android.bp b/audio/Android.bp
index 3121a36..8a1e892 100644
--- a/audio/Android.bp
+++ b/audio/Android.bp
@@ -1,6 +1,7 @@
// This is an autogenerated file, do not edit.
subdirs = [
"2.0",
+ "2.0/vts/functional",
"common/2.0",
"effect/2.0",
"effect/2.0/vts/functional",
diff --git a/audio/common/2.0/types.hal b/audio/common/2.0/types.hal
index ae7f545..93b898b 100644
--- a/audio/common/2.0/types.hal
+++ b/audio/common/2.0/types.hal
@@ -537,6 +537,7 @@
/* audio bus implemented by the audio system (e.g an MOST stereo channel) */
OUT_BUS = 0x1000000,
OUT_PROXY = 0x2000000,
+ OUT_USB_HEADSET = 0x4000000,
OUT_DEFAULT = BIT_DEFAULT,
OUT_ALL = (OUT_EARPIECE |
OUT_SPEAKER |
@@ -603,6 +604,7 @@
/* audio bus implemented by the audio system (e.g an MOST stereo channel) */
IN_BUS = BIT_IN | 0x100000,
IN_PROXY = BIT_IN | 0x1000000,
+ IN_USB_HEADSET = BIT_IN | 0x2000000,
IN_DEFAULT = BIT_IN | BIT_DEFAULT,
IN_ALL = (IN_COMMUNICATION |
@@ -670,6 +672,7 @@
DIRECT_PCM = 0x2000, // Audio stream containing PCM data that needs
// to pass through compress path for DSP post proc.
MMAP_NOIRQ = 0x4000, // output operates in MMAP no IRQ mode.
+ VOIP_CALL_RX = 0x8000, // preferred output for VoIP calls.
};
/*
@@ -680,12 +683,13 @@
*/
@export(name="audio_input_flags_t", value_prefix="AUDIO_INPUT_FLAG_")
enum AudioInputFlag : int32_t {
- NONE = 0x0, // no attributes
- FAST = 0x1, // prefer an input that supports "fast tracks"
- HW_HOTWORD = 0x2, // prefer an input that captures from hw hotword source
- RAW = 0x4, // minimize signal processing
- SYNC = 0x8, // synchronize I/O streams
- MMAP_NOIRQ = 0x10, // input operates in MMAP no IRQ mode.
+ NONE = 0x0, // no attributes
+ FAST = 0x1, // prefer an input that supports "fast tracks"
+ HW_HOTWORD = 0x2, // prefer an input that captures from hw hotword source
+ RAW = 0x4, // minimize signal processing
+ SYNC = 0x8, // synchronize I/O streams
+ MMAP_NOIRQ = 0x10, // input operates in MMAP no IRQ mode.
+ VOIP_CALL_TX = 0x20, // preferred input for VoIP calls.
};
@export(name="audio_usage_t", value_prefix="AUDIO_USAGE_")
diff --git a/automotive/vehicle/2.0/Android.mk b/automotive/vehicle/2.0/Android.mk
index f976c39..d093017 100644
--- a/automotive/vehicle/2.0/Android.mk
+++ b/automotive/vehicle/2.0/Android.mk
@@ -910,44 +910,6 @@
LOCAL_GENERATED_SOURCES += $(GEN)
#
-# Build types.hal (VmsMessageIntegerValuesIndex)
-#
-GEN := $(intermediates)/android/hardware/automotive/vehicle/V2_0/VmsMessageIntegerValuesIndex.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
- $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
- -Ljava \
- -randroid.hardware:hardware/interfaces \
- -randroid.hidl:system/libhidl/transport \
- android.hardware.automotive.vehicle@2.0::types.VmsMessageIntegerValuesIndex
-
-$(GEN): $(LOCAL_PATH)/types.hal
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-#
-# Build types.hal (VmsMessageType)
-#
-GEN := $(intermediates)/android/hardware/automotive/vehicle/V2_0/VmsMessageType.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
- $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
- -Ljava \
- -randroid.hardware:hardware/interfaces \
- -randroid.hidl:system/libhidl/transport \
- android.hardware.automotive.vehicle@2.0::types.VmsMessageType
-
-$(GEN): $(LOCAL_PATH)/types.hal
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-#
# Build types.hal (Wheel)
#
GEN := $(intermediates)/android/hardware/automotive/vehicle/V2_0/Wheel.java
@@ -1920,44 +1882,6 @@
LOCAL_GENERATED_SOURCES += $(GEN)
#
-# Build types.hal (VmsMessageIntegerValuesIndex)
-#
-GEN := $(intermediates)/android/hardware/automotive/vehicle/V2_0/VmsMessageIntegerValuesIndex.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
- $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
- -Ljava \
- -randroid.hardware:hardware/interfaces \
- -randroid.hidl:system/libhidl/transport \
- android.hardware.automotive.vehicle@2.0::types.VmsMessageIntegerValuesIndex
-
-$(GEN): $(LOCAL_PATH)/types.hal
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-#
-# Build types.hal (VmsMessageType)
-#
-GEN := $(intermediates)/android/hardware/automotive/vehicle/V2_0/VmsMessageType.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
- $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
- -Ljava \
- -randroid.hardware:hardware/interfaces \
- -randroid.hidl:system/libhidl/transport \
- android.hardware.automotive.vehicle@2.0::types.VmsMessageType
-
-$(GEN): $(LOCAL_PATH)/types.hal
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-#
# Build types.hal (Wheel)
#
GEN := $(intermediates)/android/hardware/automotive/vehicle/V2_0/Wheel.java
diff --git a/automotive/vehicle/2.0/default/Android.mk b/automotive/vehicle/2.0/default/Android.mk
index ba4a6cd..9b3323d 100644
--- a/automotive/vehicle/2.0/default/Android.mk
+++ b/automotive/vehicle/2.0/default/Android.mk
@@ -118,6 +118,7 @@
LOCAL_SRC_FILES:= \
tests/AccessControlConfigParser_test.cpp \
+ tests/RecurrentTimer_test.cpp \
tests/SubscriptionManager_test.cpp \
tests/VehicleHalManager_test.cpp \
tests/VehicleObjectPool_test.cpp \
diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/RecurrentTimer.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/RecurrentTimer.h
new file mode 100644
index 0000000..be25adc
--- /dev/null
+++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/RecurrentTimer.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef android_hardware_automotive_vehicle_V2_0_RecurrentTimer_H_
+#define android_hardware_automotive_vehicle_V2_0_RecurrentTimer_H_
+
+#include <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <functional>
+#include <list>
+#include <mutex>
+#include <set>
+#include <thread>
+#include <unordered_map>
+
+/**
+ * This class allows to specify multiple time intervals to receive
+ * notifications. A single thread is used internally.
+ */
+class RecurrentTimer {
+private:
+ using Nanos = std::chrono::nanoseconds;
+ using Clock = std::chrono::steady_clock;
+ using TimePoint = std::chrono::time_point<Clock, Nanos>;
+public:
+ using Action = std::function<void(const std::vector<int32_t>& cookies)>;
+
+ RecurrentTimer(const Action& action) : mAction(action) {
+ mTimerThread = std::thread(&RecurrentTimer::loop, this, action);
+ }
+
+ virtual ~RecurrentTimer() {
+ stop();
+ }
+
+ /**
+ * Registers recurrent event for a given interval. Registred events are distinguished by
+ * cookies thus calling this method multiple times with the same cookie will override the
+ * interval provided before.
+ */
+ void registerRecurrentEvent(std::chrono::nanoseconds interval, int32_t cookie) {
+ TimePoint now = Clock::now();
+ // Align event time point among all intervals. Thus if we have two intervals 1ms and 2ms,
+ // during every second wake-up both intervals will be triggered.
+ TimePoint absoluteTime = now - Nanos(now.time_since_epoch().count() % interval.count());
+
+ {
+ std::lock_guard<std::mutex> g(mLock);
+ mCookieToEventsMap[cookie] = { interval, cookie, absoluteTime };
+ }
+ mCond.notify_one();
+ }
+
+ void unregisterRecurrentEvent(int32_t cookie) {
+ {
+ std::lock_guard<std::mutex> g(mLock);
+ mCookieToEventsMap.erase(cookie);
+ }
+ mCond.notify_one();
+ }
+
+
+private:
+
+ struct RecurrentEvent {
+ Nanos interval;
+ int32_t cookie;
+ TimePoint absoluteTime; // Absolute time of the next event.
+
+ void updateNextEventTime(TimePoint now) {
+ // We want to move time to next event by adding some number of intervals (usually 1)
+ // to previous absoluteTime.
+ int intervalMultiplier = (now - absoluteTime) / interval;
+ if (intervalMultiplier <= 0) intervalMultiplier = 1;
+ absoluteTime += intervalMultiplier * interval;
+ }
+ };
+
+ void loop(const Action& action) {
+ static constexpr auto kInvalidTime = TimePoint(Nanos::max());
+
+ std::vector<int32_t> cookies;
+
+ while (!mStopRequested) {
+ auto now = Clock::now();
+ auto nextEventTime = kInvalidTime;
+ cookies.clear();
+
+ {
+ std::unique_lock<std::mutex> g(mLock);
+
+ for (auto&& it : mCookieToEventsMap) {
+ RecurrentEvent& event = it.second;
+ if (event.absoluteTime <= now) {
+ event.updateNextEventTime(now);
+ cookies.push_back(event.cookie);
+ }
+
+ if (nextEventTime > event.absoluteTime) {
+ nextEventTime = event.absoluteTime;
+ }
+ }
+ }
+
+ if (cookies.size() != 0) {
+ action(cookies);
+ }
+
+ std::unique_lock<std::mutex> g(mLock);
+ mCond.wait_until(g, nextEventTime); // nextEventTime can be nanoseconds::max()
+ }
+ }
+
+ void stop() {
+ mStopRequested = true;
+ {
+ std::lock_guard<std::mutex> g(mLock);
+ mCookieToEventsMap.clear();
+ }
+ mCond.notify_one();
+ if (mTimerThread.joinable()) {
+ mTimerThread.join();
+ }
+ }
+private:
+ mutable std::mutex mLock;
+ std::thread mTimerThread;
+ std::condition_variable mCond;
+ std::atomic_bool mStopRequested { false };
+ Action mAction;
+ std::unordered_map<int32_t, RecurrentEvent> mCookieToEventsMap;
+};
+
+
+#endif // android_hardware_automotive_vehicle_V2_0_RecurrentTimer_H
diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/SubscriptionManager.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/SubscriptionManager.h
index 6a12b77..a808c66 100644
--- a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/SubscriptionManager.h
+++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/SubscriptionManager.h
@@ -23,6 +23,7 @@
#include <list>
#include <android/log.h>
+#include <hidl/HidlSupport.h>
#include <hwbinder/IPCThreadState.h>
#include <android/hardware/automotive/vehicle/2.0/IVehicle.h>
@@ -50,10 +51,8 @@
}
void addOrUpdateSubscription(const SubscribeOptions &opts);
-
- bool isSubscribed(int32_t propId,
- int32_t areaId,
- SubscribeFlags flags);
+ bool isSubscribed(int32_t propId, int32_t areaId, SubscribeFlags flags);
+ std::vector<int32_t> getSubscribedProperties() const;
private:
const sp<IVehicleCallback> mCallback;
@@ -85,15 +84,29 @@
class SubscriptionManager {
public:
- virtual ~SubscriptionManager() {}
+ using OnPropertyUnsubscribed = std::function<void(int32_t)>;
+
+ /**
+ * Constructs SubscriptionManager
+ *
+ * @param onPropertyUnsubscribed - this callback function will be called when there are no
+ * more client subscribed to particular property.
+ */
+ SubscriptionManager(const OnPropertyUnsubscribed& onPropertyUnsubscribed)
+ : mOnPropertyUnsubscribed(onPropertyUnsubscribed),
+ mCallbackDeathRecipient(new DeathRecipient(
+ std::bind(&SubscriptionManager::onCallbackDead, this, std::placeholders::_1)))
+ {}
+
+ ~SubscriptionManager() = default;
/**
* Updates subscription. Returns the vector of properties subscription that
* needs to be updated in VehicleHAL.
*/
- std::list<SubscribeOptions> addOrUpdateSubscription(
- const sp<IVehicleCallback>& callback,
- const hidl_vec<SubscribeOptions>& optionList);
+ StatusCode addOrUpdateSubscription(const sp<IVehicleCallback>& callback,
+ const hidl_vec<SubscribeOptions>& optionList,
+ std::list<SubscribeOptions>* outUpdatedOptions);
/**
* Returns a list of IVehicleCallback -> list of VehiclePropValue ready for
@@ -103,30 +116,48 @@
const std::vector<recyclable_ptr<VehiclePropValue>>& propValues,
SubscribeFlags flags) const;
- std::list<sp<HalClient>> getSubscribedClients(
- int32_t propId, int32_t area, SubscribeFlags flags) const;
-
+ std::list<sp<HalClient>> getSubscribedClients(int32_t propId,
+ int32_t area,
+ SubscribeFlags flags) const;
/**
- * Returns true the client was unsubscribed successfully and there are
- * no more clients subscribed to given propId.
+ * If there are no clients subscribed to given properties than callback function provided
+ * in the constructor will be called.
*/
- bool unsubscribe(const sp<IVehicleCallback>& callback,
- int32_t propId);
+ void unsubscribe(const sp<IVehicleCallback>& callback, int32_t propId);
private:
- std::list<sp< HalClient>> getSubscribedClientsLocked(
- int32_t propId, int32_t area, SubscribeFlags flags) const;
+ std::list<sp<HalClient>> getSubscribedClientsLocked(int32_t propId,
+ int32_t area,
+ SubscribeFlags flags) const;
- bool updateHalEventSubscriptionLocked(const SubscribeOptions &opts,
- SubscribeOptions *out);
+ bool updateHalEventSubscriptionLocked(const SubscribeOptions &opts, SubscribeOptions* out);
- void addClientToPropMapLocked(int32_t propId,
- const sp<HalClient> &client);
+ void addClientToPropMapLocked(int32_t propId, const sp<HalClient>& client);
- sp<HalClientVector> getClientsForPropertyLocked(
- int32_t propId) const;
+ sp<HalClientVector> getClientsForPropertyLocked(int32_t propId) const;
- sp<HalClient> getOrCreateHalClientLocked(
- const sp<IVehicleCallback> &callback);
+ sp<HalClient> getOrCreateHalClientLocked(const sp<IVehicleCallback> &callback);
+
+ void onCallbackDead(uint64_t cookie);
+
+private:
+ using OnClientDead = std::function<void(uint64_t)>;
+
+ class DeathRecipient : public hidl_death_recipient {
+ public:
+ DeathRecipient(const OnClientDead& onClientDead)
+ : mOnClientDead(onClientDead) {}
+ ~DeathRecipient() = default;
+
+ DeathRecipient(const DeathRecipient& ) = delete;
+ DeathRecipient& operator=(const DeathRecipient&) = delete;
+
+ void serviceDied(uint64_t cookie,
+ const wp<::android::hidl::base::V1_0::IBase>& /* who */) override {
+ mOnClientDead(cookie);
+ }
+ private:
+ OnClientDead mOnClientDead;
+ };
private:
using MuxGuard = std::lock_guard<std::mutex>;
@@ -136,6 +167,9 @@
std::map<sp<IVehicleCallback>, sp<HalClient>> mClients;
std::map<int32_t, sp<HalClientVector>> mPropToClients;
std::map<int32_t, SubscribeOptions> mHalEventSubscribeOptions;
+
+ OnPropertyUnsubscribed mOnPropertyUnsubscribed;
+ sp<DeathRecipient> mCallbackDeathRecipient;
};
diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHalManager.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHalManager.h
index 4bff4d1..b8ab309 100644
--- a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHalManager.h
+++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHalManager.h
@@ -56,7 +56,9 @@
class VehicleHalManager : public IVehicle {
public:
VehicleHalManager(VehicleHal* vehicleHal)
- : mHal(vehicleHal) {
+ : mHal(vehicleHal),
+ mSubscriptionManager(std::bind(&VehicleHalManager::onAllClientsUnsubscribed,
+ this, std::placeholders::_1)) {
init();
}
@@ -105,6 +107,8 @@
int32_t propertyId,
VehiclePropertyAccess requiredAccess) const;
+ void onAllClientsUnsubscribed(int32_t propertyId);
+
static bool isSubscribable(const VehiclePropConfig& config,
SubscribeFlags flags);
static bool isSampleRateFixed(VehiclePropertyChangeMode mode);
diff --git a/automotive/vehicle/2.0/default/common/src/SubscriptionManager.cpp b/automotive/vehicle/2.0/default/common/src/SubscriptionManager.cpp
index f6f2758..4493a41 100644
--- a/automotive/vehicle/2.0/default/common/src/SubscriptionManager.cpp
+++ b/automotive/vehicle/2.0/default/common/src/SubscriptionManager.cpp
@@ -19,6 +19,7 @@
#include "SubscriptionManager.h"
#include <cmath>
+#include <inttypes.h>
#include <android/log.h>
@@ -58,6 +59,8 @@
}
void HalClient::addOrUpdateSubscription(const SubscribeOptions &opts) {
+ ALOGI("%s opts.propId: 0x%x", __func__, opts.propId);
+
auto it = mSubscriptions.find(opts.propId);
if (it == mSubscriptions.end()) {
mSubscriptions.emplace(opts.propId, opts);
@@ -84,17 +87,33 @@
return res;
}
-std::list<SubscribeOptions> SubscriptionManager::addOrUpdateSubscription(
+std::vector<int32_t> HalClient::getSubscribedProperties() const {
+ std::vector<int32_t> props;
+ for (const auto& subscription : mSubscriptions) {
+ ALOGI("%s propId: 0x%x, propId: 0x%x", __func__, subscription.first, subscription.second.propId);
+ props.push_back(subscription.first);
+ }
+ return props;
+}
+
+StatusCode SubscriptionManager::addOrUpdateSubscription(
const sp<IVehicleCallback> &callback,
- const hidl_vec<SubscribeOptions> &optionList) {
- std::list<SubscribeOptions> updatedSubscriptions;
+ const hidl_vec<SubscribeOptions> &optionList,
+ std::list<SubscribeOptions>* outUpdatedSubscriptions) {
+ outUpdatedSubscriptions->clear();
MuxGuard g(mLock);
+ ALOGI("SubscriptionManager::addOrUpdateSubscription, callback: %p", callback.get());
+
const sp<HalClient>& client = getOrCreateHalClientLocked(callback);
+ if (client.get() == nullptr) {
+ return StatusCode::INTERNAL_ERROR;
+ }
for (size_t i = 0; i < optionList.size(); i++) {
const SubscribeOptions& opts = optionList[i];
+ ALOGI("SubscriptionManager::addOrUpdateSubscription, prop: 0x%x", opts.propId);
client->addOrUpdateSubscription(opts);
addClientToPropMapLocked(opts.propId, client);
@@ -102,12 +121,12 @@
if (SubscribeFlags::HAL_EVENT & opts.flags) {
SubscribeOptions updated;
if (updateHalEventSubscriptionLocked(opts, &updated)) {
- updatedSubscriptions.push_back(updated);
+ outUpdatedSubscriptions->push_back(updated);
}
}
}
- return updatedSubscriptions;
+ return StatusCode::OK;
}
std::list<HalClientValues> SubscriptionManager::distributeValuesToClients(
@@ -205,6 +224,14 @@
const sp<IVehicleCallback>& callback) {
auto it = mClients.find(callback);
if (it == mClients.end()) {
+ uint64_t cookie = reinterpret_cast<uint64_t>(callback.get());
+ ALOGI("Creating new client and linking to death recipient, cookie: 0x%" PRIx64, cookie);
+ auto res = callback->linkToDeath(mCallbackDeathRecipient, cookie);
+ if (!res.isOk()) { // Client is already dead?
+ ALOGW("%s failed to link to death, client %p, err: %s",
+ __func__, callback.get(), res.description().c_str());
+ return nullptr;
+ }
IPCThreadState* self = IPCThreadState::self();
pid_t pid = self->getCallingPid();
uid_t uid = self->getCallingUid();
@@ -216,7 +243,7 @@
}
}
-bool SubscriptionManager::unsubscribe(const sp<IVehicleCallback>& callback,
+void SubscriptionManager::unsubscribe(const sp<IVehicleCallback>& callback,
int32_t propId) {
MuxGuard g(mLock);
auto propertyClients = getClientsForPropertyLocked(propId);
@@ -243,13 +270,39 @@
}
if (!isClientSubscribedToOtherProps) {
+ auto res = client->getCallback()->unlinkToDeath(mCallbackDeathRecipient);
+ if (!res.isOk()) {
+ ALOGW("%s failed to unlink to death, client: %p, err: %s",
+ __func__, client->getCallback().get(), res.description().c_str());
+ }
mClients.erase(clientIter);
}
}
- return (propertyClients == nullptr || propertyClients->isEmpty())
- ? mHalEventSubscribeOptions.erase(propId) == 1
- : false;
+ if (propertyClients == nullptr || propertyClients->isEmpty()) {
+ mHalEventSubscribeOptions.erase(propId);
+ mOnPropertyUnsubscribed(propId);
+ }
+}
+
+void SubscriptionManager::onCallbackDead(uint64_t cookie) {
+ ALOGI("%s, cookie: 0x%" PRIx64, __func__, cookie);
+ IVehicleCallback* callback = reinterpret_cast<IVehicleCallback*>(cookie);
+
+ std::vector<int32_t> props;
+ {
+ MuxGuard g(mLock);
+ const auto& it = mClients.find(callback);
+ if (it == mClients.end()) {
+ return; // Nothing to do here, client wasn't subscribed to any properties.
+ }
+ const auto& halClient = it->second;
+ props = halClient->getSubscribedProperties();
+ }
+
+ for (int32_t propId : props) {
+ unsubscribe(callback, propId);
+ }
}
diff --git a/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp b/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp
index 3a5e504..8906f6e 100644
--- a/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp
+++ b/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp
@@ -43,8 +43,7 @@
*/
constexpr auto kMaxHidlVecOfVehiclPropValuePoolSize = 20;
-Return<void> VehicleHalManager::getAllPropConfigs(
- getAllPropConfigs_cb _hidl_cb) {
+Return<void> VehicleHalManager::getAllPropConfigs(getAllPropConfigs_cb _hidl_cb) {
ALOGI("getAllPropConfigs called");
hidl_vec<VehiclePropConfig> hidlConfigs;
auto& halConfig = mConfigIndex->getAllConfigs();
@@ -58,9 +57,8 @@
return Void();
}
-Return<void> VehicleHalManager::getPropConfigs(
- const hidl_vec<int32_t> &properties,
- getPropConfigs_cb _hidl_cb) {
+Return<void> VehicleHalManager::getPropConfigs(const hidl_vec<int32_t> &properties,
+ getPropConfigs_cb _hidl_cb) {
std::vector<VehiclePropConfig> configs;
for (size_t i = 0; i < properties.size(); i++) {
auto prop = properties[i];
@@ -77,8 +75,7 @@
return Void();
}
-Return<void> VehicleHalManager::get(
- const VehiclePropValue& requestedPropValue, get_cb _hidl_cb) {
+Return<void> VehicleHalManager::get(const VehiclePropValue& requestedPropValue, get_cb _hidl_cb) {
const auto* config = getPropConfigOrNull(requestedPropValue.prop);
if (config == nullptr) {
ALOGE("Failed to get value: config not found, property: 0x%x",
@@ -119,9 +116,8 @@
return Return<StatusCode>(status);
}
-Return<StatusCode> VehicleHalManager::subscribe(
- const sp<IVehicleCallback> &callback,
- const hidl_vec<SubscribeOptions> &options) {
+Return<StatusCode> VehicleHalManager::subscribe(const sp<IVehicleCallback> &callback,
+ const hidl_vec<SubscribeOptions> &options) {
hidl_vec<SubscribeOptions> verifiedOptions(options);
auto caller = getCaller();
for (size_t i = 0; i < verifiedOptions.size(); i++) {
@@ -135,6 +131,11 @@
return StatusCode::INVALID_ARG;
}
+ if (ops.flags == SubscribeFlags::UNDEFINED) {
+ ALOGE("Failed to subscribe: undefined flag in options provided");
+ return StatusCode::INVALID_ARG;
+ }
+
if (!checkAcl(caller.uid, config->prop, VehiclePropertyAccess::READ)) {
return StatusCode::ACCESS_DENIED;
}
@@ -157,22 +158,24 @@
ops.sampleRate = checkSampleRate(*config, ops.sampleRate);
}
- std::list<SubscribeOptions> updatedOptions =
- mSubscriptionManager.addOrUpdateSubscription(callback, verifiedOptions);
+ std::list<SubscribeOptions> updatedOptions;
+ auto res = mSubscriptionManager.addOrUpdateSubscription(callback, verifiedOptions,
+ &updatedOptions);
+ if (StatusCode::OK != res) {
+ ALOGW("%s failed to subscribe, error code: %d", __func__, res);
+ return res;
+ }
for (auto opt : updatedOptions) {
mHal->subscribe(opt.propId, opt.vehicleAreas, opt.sampleRate);
}
- // TODO(pavelm): link to death callback (not implemented yet in HIDL)
return StatusCode::OK;
}
-Return<StatusCode> VehicleHalManager::unsubscribe(
- const sp<IVehicleCallback>& callback, int32_t propId) {
- if (mSubscriptionManager.unsubscribe(callback, propId)) {
- mHal->unsubscribe(propId);
- }
+Return<StatusCode> VehicleHalManager::unsubscribe(const sp<IVehicleCallback>& callback,
+ int32_t propId) {
+ mSubscriptionManager.unsubscribe(callback, propId);
return StatusCode::OK;
}
@@ -239,8 +242,7 @@
}
}
-void VehicleHalManager::onBatchHalEvent(
- const std::vector<VehiclePropValuePtr>& values) {
+void VehicleHalManager::onBatchHalEvent(const std::vector<VehiclePropValuePtr>& values) {
const auto& clientValues = mSubscriptionManager.distributeValuesToClients(
values, SubscribeFlags::HAL_EVENT);
@@ -257,7 +259,12 @@
for (VehiclePropValue* pValue : cv.values) {
shallowCopy(&(vec)[i++], *pValue);
}
- cv.client->getCallback()->onPropertyEvent(vec);
+ auto status = cv.client->getCallback()->onPropertyEvent(vec);
+ if (!status.isOk()) {
+ ALOGE("Failed to notify client %s, err: %s",
+ toString(cv.client->getCallback()).c_str(),
+ status.description().c_str());
+ }
}
}
@@ -379,6 +386,10 @@
}
}
+void VehicleHalManager::onAllClientsUnsubscribed(int32_t propertyId) {
+ mHal->unsubscribe(propertyId);
+}
+
} // namespace V2_0
} // namespace vehicle
} // namespace automotive
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
index 3c17183..596ad85 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
@@ -28,6 +28,11 @@
namespace impl {
+const VehicleProperty kHvacPowerProperties[] = {
+ VehicleProperty::HVAC_FAN_SPEED,
+ VehicleProperty::HVAC_FAN_DIRECTION,
+};
+
const VehiclePropConfig kVehicleProperties[] = {
{
.prop = toInt(VehicleProperty::INFO_MAKE),
@@ -63,7 +68,10 @@
.prop = toInt(VehicleProperty::HVAC_POWER_ON),
.access = VehiclePropertyAccess::READ_WRITE,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .supportedAreas = toInt(VehicleAreaZone::ROW_1)
+ .supportedAreas = toInt(VehicleAreaZone::ROW_1),
+ // TODO(bryaneyler): Ideally, this is generated dynamically from
+ // kHvacPowerProperties.
+ .configString = "0x12400500,0x12400501" // HVAC_FAN_SPEED,HVAC_FAN_DIRECTION
},
{
@@ -139,6 +147,15 @@
},
{
+ .prop = toInt(VehicleProperty::ENV_OUTSIDE_TEMPERATURE),
+ .access = VehiclePropertyAccess::READ,
+ // TODO(bryaneyler): Support ON_CHANGE as well.
+ .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
+ .minSampleRate = 1.0f,
+ .maxSampleRate = 2.0f,
+ },
+
+ {
.prop = toInt(VehicleProperty::NIGHT_MODE),
.access = VehiclePropertyAccess::READ,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
@@ -184,6 +201,14 @@
.prop = toInt(VehicleProperty::IGNITION_STATE),
.access = VehiclePropertyAccess::READ,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+ },
+
+ {
+ .prop = toInt(VehicleProperty::ENGINE_OIL_TEMP),
+ .access = VehiclePropertyAccess::READ,
+ .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
+ .minSampleRate = 0.1, // 0.1 Hz, every 10 seconds
+ .maxSampleRate = 10, // 10 Hz, every 100 ms
}
};
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp
index 38e21c7..2214100 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp
@@ -102,9 +102,9 @@
{
std::lock_guard<std::mutex> lock(mPropsMutex);
- for (auto& propVal : mProps) {
+ for (auto& prop : mProps) {
emulator::VehiclePropValue* protoVal = respMsg.add_value();
- populateProtoVehiclePropValue(protoVal, propVal.get());
+ populateProtoVehiclePropValue(protoVal, prop.second.get());
}
}
}
@@ -118,6 +118,7 @@
val.prop = protoVal.prop();
val.areaId = protoVal.area_id();
+ val.timestamp = elapsedRealtimeNano();
// Copy value data if it is set. This automatically handles complex data types if needed.
if (protoVal.has_string_value()) {
@@ -172,12 +173,11 @@
areaId = 0;
}
- for (auto& prop : mProps) {
- if ((prop->prop == propId) && (prop->areaId == areaId)) {
- return prop.get();
- }
+ auto prop = mProps.find(std::make_pair(propId, areaId));
+ if (prop != mProps.end()) {
+ return prop->second.get();
}
- ALOGW("%s: Property not found: propId = 0x%x, areaId = 0x%x", __FUNCTION__, propId, areaId);
+ ALOGW("%s: Property not found: propId = 0x%x, areaId = 0x%x", __func__, propId, areaId);
return nullptr;
}
@@ -203,7 +203,7 @@
doSetProperty(rxMsg, respMsg);
break;
default:
- ALOGW("%s: Unknown message received, type = %d", __FUNCTION__, rxMsg.msg_type());
+ ALOGW("%s: Unknown message received, type = %d", __func__, rxMsg.msg_type());
respMsg.set_status(emulator::ERROR_UNIMPLEMENTED_CMD);
break;
}
@@ -211,7 +211,7 @@
// Send the reply
txMsg(respMsg);
} else {
- ALOGE("%s: ParseFromString() failed. msgSize=%d", __FUNCTION__, static_cast<int>(msg.size()));
+ ALOGE("%s: ParseFromString() failed. msgSize=%d", __func__, static_cast<int>(msg.size()));
}
}
@@ -267,7 +267,7 @@
}
break;
default:
- ALOGW("%s: Unknown property type: 0x%x", __FUNCTION__, toInt(getPropType(cfg.prop)));
+ ALOGW("%s: Unknown property type: 0x%x", __func__, toInt(getPropType(cfg.prop)));
break;
}
@@ -318,7 +318,7 @@
parseRxProtoBuf(msg);
} else {
// This happens when connection is closed
- ALOGD("%s: numBytes=%d, msgSize=%d", __FUNCTION__, numBytes,
+ ALOGD("%s: numBytes=%d, msgSize=%d", __func__, numBytes,
static_cast<int32_t>(msg.size()));
break;
}
@@ -397,6 +397,9 @@
case toInt(VehicleProperty::HVAC_TEMPERATURE_SET):
prop->value.floatValues[0] = 16;
break;
+ case toInt(VehicleProperty::ENV_OUTSIDE_TEMPERATURE):
+ prop->value.floatValues[0] = 25;
+ break;
case toInt(VehicleProperty::NIGHT_MODE):
prop->value.int32Values[0] = 0;
break;
@@ -409,6 +412,9 @@
case toInt(VehicleProperty::INFO_FUEL_CAPACITY):
prop->value.floatValues[0] = 0.75f;
break;
+ case toInt(VehicleProperty::ENGINE_OIL_TEMP):
+ prop->value.floatValues[0] = 101;
+ break;
case toInt(VehicleProperty::DISPLAY_BRIGHTNESS):
prop->value.int32Values[0] = 7;
break;
@@ -416,7 +422,7 @@
prop->value.int32Values[0] = toInt(VehicleIgnitionState::ON);
break;
default:
- ALOGW("%s: propId=0x%x not found", __FUNCTION__, prop->prop);
+ ALOGW("%s: propId=0x%x not found", __func__, prop->prop);
break;
}
}
@@ -435,10 +441,10 @@
}
if (retVal < 0) {
- ALOGE("%s: Failed to tx message: retval=%d, errno=%d", __FUNCTION__, retVal, errno);
+ ALOGE("%s: Failed to tx message: retval=%d, errno=%d", __func__, retVal, errno);
}
} else {
- ALOGE("%s: SerializeToString failed!", __FUNCTION__);
+ ALOGE("%s: SerializeToString failed!", __func__);
}
}
@@ -454,7 +460,7 @@
VehiclePropValue* internalPropValue = getVehiclePropValueLocked(propId, areaId);
if (internalPropValue != nullptr) {
internalPropValue->value = propValue.value;
- internalPropValue->timestamp = elapsedRealtimeNano();
+ internalPropValue->timestamp = propValue.timestamp;
status = StatusCode::OK;
}
}
@@ -497,6 +503,18 @@
StatusCode status;
switch (propId) {
default:
+ if (mHvacPowerProps.find(VehicleProperty(propId)) !=
+ mHvacPowerProps.end()) {
+ auto prop = mProps.find(
+ std::make_pair(toInt(VehicleProperty::HVAC_POWER_ON), 0));
+ if (prop != mProps.end()) {
+ if (prop->second->value.int32Values.size() == 1 &&
+ prop->second->value.int32Values[0] == 0) {
+ status = StatusCode::NOT_AVAILABLE;
+ break;
+ }
+ }
+ }
status = updateProperty(propValue);
if (status == StatusCode::OK) {
// Send property update to emulator
@@ -518,6 +536,10 @@
// Initialize member variables
mExit = 0;
+ for (auto& prop : kHvacPowerProperties) {
+ mHvacPowerProps.insert(prop);
+ }
+
// Get the list of configurations supported by this HAL
std::vector<VehiclePropConfig> configs = listProperties();
@@ -551,10 +573,8 @@
break;
}
continue;
- break;
- case VehiclePropertyType::MASK:
default:
- ALOGW("%s: propType=0x%x not found", __FUNCTION__, propType);
+ ALOGE("%s: propType=0x%x not found", __func__, propType);
vecSize = 0;
break;
}
@@ -579,7 +599,7 @@
prop->areaId = curArea;
prop->prop = cfg.prop;
setDefaultValue(prop.get());
- mProps.push_back(std::move(prop));
+ mProps[std::make_pair(prop->prop, prop->areaId)] = std::move(prop);
} while (supportedAreas != 0);
}
@@ -587,6 +607,69 @@
mThread = std::thread(&DefaultVehicleHal::rxThread, this);
}
+void DefaultVehicleHal::onContinuousPropertyTimer(const std::vector<int32_t>& properties) {
+ VehiclePropValuePtr v;
+
+ auto& pool = *getValuePool();
+
+ for (int32_t property : properties) {
+ if (isContinuousProperty(property)) {
+ // In real implementation this value should be read from sensor, random
+ // value used for testing purpose only.
+ std::lock_guard<std::mutex> lock(mPropsMutex);
+
+ VehiclePropValue *internalPropValue = getVehiclePropValueLocked(property);
+ if (internalPropValue != nullptr) {
+ v = pool.obtain(*internalPropValue);
+ }
+ if (VehiclePropertyType::FLOAT == getPropType(property)) {
+ // Just get some randomness to continuous properties to see slightly differnt values
+ // on the other end.
+ v->value.floatValues[0] = v->value.floatValues[0] + std::rand() % 5;
+ }
+ } else {
+ ALOGE("Unexpected onContinuousPropertyTimer for property: 0x%x", property);
+ }
+
+ if (v.get()) {
+ v->timestamp = elapsedRealtimeNano();
+ doHalEvent(std::move(v));
+ }
+ }
+}
+
+StatusCode DefaultVehicleHal::subscribe(int32_t property, int32_t,
+ float sampleRate) {
+ ALOGI("subscribe called for property: 0x%x, sampleRate: %f", property, sampleRate);
+
+ if (isContinuousProperty(property)) {
+ mRecurrentTimer.registerRecurrentEvent(hertzToNanoseconds(sampleRate), property);
+ }
+ return StatusCode::OK;
+}
+
+StatusCode DefaultVehicleHal::unsubscribe(int32_t property) {
+ ALOGI("%s propId: 0x%x", __func__, property);
+ if (isContinuousProperty(property)) {
+ mRecurrentTimer.unregisterRecurrentEvent(property);
+ }
+ return StatusCode::OK;
+}
+
+const VehiclePropConfig* DefaultVehicleHal::getPropConfig(int32_t propId) const {
+ auto it = mPropConfigMap.find(propId);
+ return it == mPropConfigMap.end() ? nullptr : it->second;
+}
+
+bool DefaultVehicleHal::isContinuousProperty(int32_t propId) const {
+ const VehiclePropConfig* config = getPropConfig(propId);
+ if (config == nullptr) {
+ ALOGW("Config not found for property: 0x%x", propId);
+ return false;
+ }
+ return config->changeMode == VehiclePropertyChangeMode::CONTINUOUS;
+}
+
} // impl
} // namespace V2_0
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h
index b4ba8ba..98eef27 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h
@@ -17,15 +17,18 @@
#ifndef android_hardware_automotive_vehicle_V2_0_impl_DefaultVehicleHal_H_
#define android_hardware_automotive_vehicle_V2_0_impl_DefaultVehicleHal_H_
+#include <map>
#include <memory>
#include <sys/socket.h>
#include <thread>
+#include <unordered_set>
#include <utils/SystemClock.h>
#include "CommBase.h"
#include "VehicleHalProto.pb.h"
+#include <vhal_v2_0/RecurrentTimer.h>
#include <vhal_v2_0/VehicleHal.h>
#include "DefaultConfig.h"
@@ -41,7 +44,13 @@
class DefaultVehicleHal : public VehicleHal {
public:
- DefaultVehicleHal() : mThread() {}
+ DefaultVehicleHal() : mRecurrentTimer(
+ std::bind(&DefaultVehicleHal::onContinuousPropertyTimer, this, std::placeholders::_1)) {
+ for (size_t i = 0; i < arraysize(kVehicleProperties); i++) {
+ mPropConfigMap[kVehicleProperties->prop] = &kVehicleProperties[i];
+ }
+ }
+
~DefaultVehicleHal() override {
// Notify thread to finish and wait for it to terminate
mExit = 1;
@@ -64,16 +73,9 @@
StatusCode set(const VehiclePropValue& propValue) override;
- StatusCode subscribe(int32_t property, int32_t areas, float sampleRate) {
- ALOGD("%s: not implemented: prop=0x%x, areas=0x%x, rate=%f", __FUNCTION__, property,
- areas, sampleRate);
- return StatusCode::OK;
- }
+ StatusCode subscribe(int32_t property, int32_t areas, float sampleRate) override;
- StatusCode unsubscribe(int32_t property) {
- ALOGD("%s: not implemented: prop=0x%x", __FUNCTION__, property);
- return StatusCode::OK;
- }
+ StatusCode unsubscribe(int32_t property) override;
private:
void doGetConfig(emulator::EmulatorMessage& rxMsg, emulator::EmulatorMessage& respMsg);
@@ -81,7 +83,9 @@
void doGetProperty(emulator::EmulatorMessage& rxMsg, emulator::EmulatorMessage& respMsg);
void doGetPropertyAll(emulator::EmulatorMessage& rxMsg, emulator::EmulatorMessage& respMsg);
void doSetProperty(emulator::EmulatorMessage& rxMsg, emulator::EmulatorMessage& respMsg);
- VehiclePropValue* getVehiclePropValueLocked(int32_t propId, int32_t areaId);
+ VehiclePropValue* getVehiclePropValueLocked(int32_t propId, int32_t areaId = 0);
+ const VehiclePropConfig* getPropConfig(int32_t propId) const;
+ bool isContinuousProperty(int32_t propId) const;
void parseRxProtoBuf(std::vector<uint8_t>& msg);
void populateProtoVehicleConfig(emulator::VehiclePropConfig* protoCfg,
const VehiclePropConfig& cfg);
@@ -92,13 +96,24 @@
void rxThread();
void txMsg(emulator::EmulatorMessage& txMsg);
StatusCode updateProperty(const VehiclePropValue& propValue);
+
+ constexpr std::chrono::nanoseconds hertzToNanoseconds(float hz) const {
+ return std::chrono::nanoseconds(static_cast<int64_t>(1000000000L / hz));
+ }
+
+ void onContinuousPropertyTimer(const std::vector<int32_t>& properties);
+
private:
- // TODO: Use a hashtable to support indexing props
- std::vector<std::unique_ptr<VehiclePropValue>> mProps;
+ std::map<
+ std::pair<int32_t /*VehicleProperty*/, int32_t /*areaId*/>,
+ std::unique_ptr<VehiclePropValue>> mProps;
std::atomic<int> mExit;
+ std::unordered_set<VehicleProperty> mHvacPowerProps;
std::mutex mPropsMutex;
std::thread mThread;
std::unique_ptr<CommBase> mComm{nullptr};
+ RecurrentTimer mRecurrentTimer;
+ std::unordered_map<int32_t, const VehiclePropConfig*> mPropConfigMap;
};
} // impl
diff --git a/automotive/vehicle/2.0/default/tests/RecurrentTimer_test.cpp b/automotive/vehicle/2.0/default/tests/RecurrentTimer_test.cpp
new file mode 100644
index 0000000..9353baa
--- /dev/null
+++ b/automotive/vehicle/2.0/default/tests/RecurrentTimer_test.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 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 <thread>
+
+#include <gtest/gtest.h>
+
+#include "vhal_v2_0/RecurrentTimer.h"
+
+namespace {
+
+using std::chrono::nanoseconds;
+using std::chrono::milliseconds;
+
+#define ASSERT_EQ_WITH_TOLERANCE(val1, val2, tolerance) \
+ASSERT_LE(val1 - tolerance, val2); \
+ASSERT_GE(val1 + tolerance, val2); \
+
+
+TEST(RecurrentTimerTest, oneInterval) {
+ std::atomic<int64_t> counter { 0L };
+ auto counterRef = std::ref(counter);
+ RecurrentTimer timer([&counterRef](const std::vector<int32_t>& cookies) {
+ ASSERT_EQ(1u, cookies.size());
+ ASSERT_EQ(0xDeadBeef, cookies.front());
+ counterRef.get()++;
+ });
+
+ timer.registerRecurrentEvent(milliseconds(1), 0xDeadBeef);
+ std::this_thread::sleep_for(milliseconds(100));
+ ASSERT_EQ_WITH_TOLERANCE(100, counter.load(), 20);
+}
+
+TEST(RecurrentTimerTest, multipleIntervals) {
+ std::atomic<int64_t> counter1ms { 0L };
+ std::atomic<int64_t> counter5ms { 0L };
+ auto counter1msRef = std::ref(counter1ms);
+ auto counter5msRef = std::ref(counter5ms);
+ RecurrentTimer timer(
+ [&counter1msRef, &counter5msRef](const std::vector<int32_t>& cookies) {
+ for (int32_t cookie : cookies) {
+ if (cookie == 0xdead) {
+ counter1msRef.get()++;
+ } else if (cookie == 0xbeef) {
+ counter5msRef.get()++;
+ } else {
+ FAIL();
+ }
+ }
+ });
+
+ timer.registerRecurrentEvent(milliseconds(1), 0xdead);
+ timer.registerRecurrentEvent(milliseconds(5), 0xbeef);
+
+ std::this_thread::sleep_for(milliseconds(100));
+ ASSERT_EQ_WITH_TOLERANCE(100, counter1ms.load(), 20);
+ ASSERT_EQ_WITH_TOLERANCE(20, counter5ms.load(), 5);
+}
+
+} // anonymous namespace
diff --git a/automotive/vehicle/2.0/default/tests/SubscriptionManager_test.cpp b/automotive/vehicle/2.0/default/tests/SubscriptionManager_test.cpp
index e13d003..7ec9b79 100644
--- a/automotive/vehicle/2.0/default/tests/SubscriptionManager_test.cpp
+++ b/automotive/vehicle/2.0/default/tests/SubscriptionManager_test.cpp
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-#include <unordered_map>
+#include <functional>
#include <iostream>
+#include <unordered_map>
#include <gtest/gtest.h>
@@ -35,8 +36,9 @@
class SubscriptionManagerTest : public ::testing::Test {
public:
- SubscriptionManager manager;
+ SubscriptionManagerTest() : manager(([this](int x) { onPropertyUnsubscribed(x); })) {}
+ SubscriptionManager manager;
static constexpr int32_t PROP1 = toInt(VehicleProperty::HVAC_FAN_SPEED);
static constexpr int32_t PROP2 = toInt(VehicleProperty::DISPLAY_BRIGHTNESS);
@@ -44,6 +46,10 @@
sp<IVehicleCallback> cb2 = new MockedVehicleCallback();
sp<IVehicleCallback> cb3 = new MockedVehicleCallback();
+ void SetUp() override {
+ lastUnsubscribedProperty = -1;
+ }
+
hidl_vec<SubscribeOptions> subscrToProp1 = {
SubscribeOptions {
.propId = PROP1,
@@ -90,12 +96,31 @@
return manager.getSubscribedClients(PROP2, 0,
SubscribeFlags::DEFAULT);
}
+
+ void onPropertyUnsubscribed(int propertyId) {
+ // Called when there are no clients who subscribed to particular property. This can happen
+ // because of explict unsubscribe call or when client (IVehicleCallback) was disconnected.
+ lastUnsubscribedProperty = propertyId;
+ }
+
+ void assertOnPropertyUnsubscribedNotCalled() {
+ ASSERT_EQ(-1, lastUnsubscribedProperty);
+ }
+
+ void assertLastUnsubscribedProperty(int expectedPropertyId) {
+ ASSERT_EQ(expectedPropertyId, lastUnsubscribedProperty);
+ lastUnsubscribedProperty = -1;
+ }
+
+private:
+ int lastUnsubscribedProperty;
};
TEST_F(SubscriptionManagerTest, multipleClients) {
- manager.addOrUpdateSubscription(cb1, subscrToProp1);
- manager.addOrUpdateSubscription(cb2, subscrToProp1);
+ std::list<SubscribeOptions> updatedOptions;
+ ASSERT_EQ(StatusCode::OK, manager.addOrUpdateSubscription(cb1, subscrToProp1, &updatedOptions));
+ ASSERT_EQ(StatusCode::OK, manager.addOrUpdateSubscription(cb2, subscrToProp1, &updatedOptions));
auto clients = manager.getSubscribedClients(
PROP1,
@@ -106,7 +131,8 @@
}
TEST_F(SubscriptionManagerTest, negativeCases) {
- manager.addOrUpdateSubscription(cb1, subscrToProp1);
+ std::list<SubscribeOptions> updatedOptions;
+ ASSERT_EQ(StatusCode::OK, manager.addOrUpdateSubscription(cb1, subscrToProp1, &updatedOptions));
// Wrong zone
auto clients = manager.getSubscribedClients(
@@ -131,7 +157,8 @@
}
TEST_F(SubscriptionManagerTest, mulipleSubscriptions) {
- manager.addOrUpdateSubscription(cb1, subscrToProp1);
+ std::list<SubscribeOptions> updatedOptions;
+ ASSERT_EQ(StatusCode::OK, manager.addOrUpdateSubscription(cb1, subscrToProp1, &updatedOptions));
auto clients = manager.getSubscribedClients(
PROP1,
@@ -142,13 +169,13 @@
// Same property, but different zone, to make sure we didn't unsubscribe
// from previous zone.
- manager.addOrUpdateSubscription(cb1, {
+ ASSERT_EQ(StatusCode::OK, manager.addOrUpdateSubscription(cb1, {
SubscribeOptions {
.propId = PROP1,
.vehicleAreas = toInt(VehicleAreaZone::ROW_2),
.flags = SubscribeFlags::DEFAULT
}
- });
+ }, &updatedOptions));
clients = manager.getSubscribedClients(PROP1,
toInt(VehicleAreaZone::ROW_1_LEFT),
@@ -162,31 +189,38 @@
}
TEST_F(SubscriptionManagerTest, unsubscribe) {
- manager.addOrUpdateSubscription(cb1, subscrToProp1);
- manager.addOrUpdateSubscription(cb2, subscrToProp2);
- manager.addOrUpdateSubscription(cb3, subscrToProp1and2);
+ std::list<SubscribeOptions> updatedOptions;
+ ASSERT_EQ(StatusCode::OK, manager.addOrUpdateSubscription(cb1, subscrToProp1, &updatedOptions));
+ ASSERT_EQ(StatusCode::OK, manager.addOrUpdateSubscription(cb2, subscrToProp2, &updatedOptions));
+ ASSERT_EQ(StatusCode::OK, manager.addOrUpdateSubscription(cb3, subscrToProp1and2,
+ &updatedOptions));
ASSERT_ALL_EXISTS({cb1, cb3}, extractCallbacks(clientsToProp1()));
ASSERT_ALL_EXISTS({cb2, cb3}, extractCallbacks(clientsToProp2()));
- ASSERT_FALSE(manager.unsubscribe(cb1, PROP1));
+ manager.unsubscribe(cb1, PROP1);
+ assertOnPropertyUnsubscribedNotCalled();
ASSERT_ALL_EXISTS({cb3}, extractCallbacks(clientsToProp1()));
// Make sure nothing changed in PROP2 so far.
ASSERT_ALL_EXISTS({cb2, cb3}, extractCallbacks(clientsToProp2()));
// No one subscribed to PROP1, subscription for PROP2 is not affected.
- ASSERT_TRUE(manager.unsubscribe(cb3, PROP1));
+ manager.unsubscribe(cb3, PROP1);
+ assertLastUnsubscribedProperty(PROP1);
ASSERT_ALL_EXISTS({cb2, cb3}, extractCallbacks(clientsToProp2()));
- ASSERT_FALSE(manager.unsubscribe(cb3, PROP2));
+ manager.unsubscribe(cb3, PROP2);
+ assertOnPropertyUnsubscribedNotCalled();
ASSERT_ALL_EXISTS({cb2}, extractCallbacks(clientsToProp2()));
// The last client unsubscribed from this property.
- ASSERT_TRUE(manager.unsubscribe(cb2, PROP2));
+ manager.unsubscribe(cb2, PROP2);
+ assertLastUnsubscribedProperty(PROP2);
- // No one was subscribed, return false.
- ASSERT_FALSE(manager.unsubscribe(cb1, PROP1));
+ // No one subscribed anymore
+ manager.unsubscribe(cb1, PROP1);
+ assertLastUnsubscribedProperty(PROP1);
}
} // namespace anonymous
diff --git a/automotive/vehicle/2.0/default/tests/VehicleHalManager_test.cpp b/automotive/vehicle/2.0/default/tests/VehicleHalManager_test.cpp
index f637344..b5cdf5c 100644
--- a/automotive/vehicle/2.0/default/tests/VehicleHalManager_test.cpp
+++ b/automotive/vehicle/2.0/default/tests/VehicleHalManager_test.cpp
@@ -68,15 +68,16 @@
pValue = getValuePool()->obtainFloat(42.42);
}
break;
- case VehicleProperty::VEHICLE_MAP_SERVICE:
- pValue = getValuePool()->obtainComplex();
- pValue->value.int32Values = hidl_vec<int32_t> { 10, 20 };
- pValue->value.int64Values = hidl_vec<int64_t> { 30, 40 };
- pValue->value.floatValues = hidl_vec<float_t> { 1.1, 2.2 };
- pValue->value.bytes = hidl_vec<uint8_t> { 1, 2, 3 };
- pValue->value.stringValue = kCarMake;
- break;
default:
+ if (requestedPropValue.prop == kCustomComplexProperty) {
+ pValue = getValuePool()->obtainComplex();
+ pValue->value.int32Values = hidl_vec<int32_t> { 10, 20 };
+ pValue->value.int64Values = hidl_vec<int64_t> { 30, 40 };
+ pValue->value.floatValues = hidl_vec<float_t> { 1.1, 2.2 };
+ pValue->value.bytes = hidl_vec<uint8_t> { 1, 2, 3 };
+ pValue->value.stringValue = kCarMake;
+ break;
+ }
auto key = makeKey(toInt(property), areaId);
if (mValues.count(key) == 0) {
ALOGW("");
@@ -318,10 +319,10 @@
}
TEST_F(VehicleHalManagerTest, get_Complex) {
- invokeGet(toInt(VehicleProperty::VEHICLE_MAP_SERVICE), 0);
+ invokeGet(kCustomComplexProperty, 0);
ASSERT_EQ(StatusCode::OK, actualStatusCode);
- ASSERT_EQ(toInt(VehicleProperty::VEHICLE_MAP_SERVICE), actualValue.prop);
+ ASSERT_EQ(kCustomComplexProperty, actualValue.prop);
ASSERT_EQ(3u, actualValue.value.bytes.size());
ASSERT_EQ(1, actualValue.value.bytes[0]);
diff --git a/automotive/vehicle/2.0/default/tests/VehicleHalTestUtils.h b/automotive/vehicle/2.0/default/tests/VehicleHalTestUtils.h
index ce1ed7d..28e1a5a 100644
--- a/automotive/vehicle/2.0/default/tests/VehicleHalTestUtils.h
+++ b/automotive/vehicle/2.0/default/tests/VehicleHalTestUtils.h
@@ -29,6 +29,11 @@
namespace vehicle {
namespace V2_0 {
+constexpr int32_t kCustomComplexProperty = 0xbeef
+ | VehiclePropertyGroup::VENDOR
+ | VehiclePropertyType::COMPLEX
+ | VehicleArea::GLOBAL;
+
const VehiclePropConfig kVehicleProperties[] = {
{
.prop = toInt(VehicleProperty::INFO_MAKE),
@@ -109,7 +114,7 @@
// Complex data type.
{
- .prop = toInt(VehicleProperty::VEHICLE_MAP_SERVICE),
+ .prop = kCustomComplexProperty,
.access = VehiclePropertyAccess::READ_WRITE,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE
}
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index 0d2dfdd..b33b6ee 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -1790,26 +1790,6 @@
| VehiclePropertyGroup:SYSTEM
| VehiclePropertyType:BOOLEAN
| VehicleArea:GLOBAL),
-
- /*
- * Vehicle Maps Service (VMS) message
- *
- * This property uses COMPLEX data to communicate vms messages.
- *
- * Its contents are to be interpreted as follows:
- * the indices defined in VmsMessageIntegerValuesIndex are to be used to
- * read from int32Values;
- * bytes is a serialized VMS message as defined in the vms protocol
- * which is opaque to the framework;
- *
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
- */
- VEHICLE_MAP_SERVICE = (
- 0x0C00
- | VehiclePropertyGroup:SYSTEM
- | VehiclePropertyType:COMPLEX
- | VehicleArea:GLOBAL),
};
/*
@@ -2707,35 +2687,3 @@
LEFT_REAR = 0x4,
RIGHT_REAR = 0x8,
};
-
-/*
- * This enum lists the types of supported VMS messages.
- */
-enum VmsMessageType : int32_t {
- /* A client subscribes to a layer. */
- SUBSCRIBE = 1,
-
- /* A client unsubscribes from a layer. */
- UNSUBSCRIBE = 2,
-
- /* A client publishes a data packet. */
- DATA = 3,
-};
-
-/*
- * This enum provides the canonical mapping for VMS properties that have an
- * integer value.
- */
-enum VmsMessageIntegerValuesIndex : int32_t {
- /* The message type as enumerated by VmsMessageType enum. */
- VMS_MESSAGE_TYPE = 0,
-
- /* The layer ID as defined in the vms protocol. */
- VMS_LAYER_ID = 1,
-
- /* The version of the VMS layer. */
- VMS_LAYER_VERSION = 2,
-
- /* The number of bytes in the payload */
- VMS_PAYLOAD_SIZE_BYTES = 3,
-};
diff --git a/automotive/vehicle/2.1/Android.mk b/automotive/vehicle/2.1/Android.mk
index f5a121d..f030af08 100644
--- a/automotive/vehicle/2.1/Android.mk
+++ b/automotive/vehicle/2.1/Android.mk
@@ -208,6 +208,44 @@
LOCAL_GENERATED_SOURCES += $(GEN)
#
+# Build types.hal (VmsMessageIntegerValuesIndex)
+#
+GEN := $(intermediates)/android/hardware/automotive/vehicle/V2_1/VmsMessageIntegerValuesIndex.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+ $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+ -Ljava \
+ -randroid.hardware:hardware/interfaces \
+ -randroid.hidl:system/libhidl/transport \
+ android.hardware.automotive.vehicle@2.1::types.VmsMessageIntegerValuesIndex
+
+$(GEN): $(LOCAL_PATH)/types.hal
+ $(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+#
+# Build types.hal (VmsMessageType)
+#
+GEN := $(intermediates)/android/hardware/automotive/vehicle/V2_1/VmsMessageType.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+ $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+ -Ljava \
+ -randroid.hardware:hardware/interfaces \
+ -randroid.hidl:system/libhidl/transport \
+ android.hardware.automotive.vehicle@2.1::types.VmsMessageType
+
+$(GEN): $(LOCAL_PATH)/types.hal
+ $(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+#
# Build IVehicle.hal
#
GEN := $(intermediates)/android/hardware/automotive/vehicle/V2_1/IVehicle.java
@@ -434,6 +472,44 @@
LOCAL_GENERATED_SOURCES += $(GEN)
#
+# Build types.hal (VmsMessageIntegerValuesIndex)
+#
+GEN := $(intermediates)/android/hardware/automotive/vehicle/V2_1/VmsMessageIntegerValuesIndex.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+ $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+ -Ljava \
+ -randroid.hardware:hardware/interfaces \
+ -randroid.hidl:system/libhidl/transport \
+ android.hardware.automotive.vehicle@2.1::types.VmsMessageIntegerValuesIndex
+
+$(GEN): $(LOCAL_PATH)/types.hal
+ $(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+#
+# Build types.hal (VmsMessageType)
+#
+GEN := $(intermediates)/android/hardware/automotive/vehicle/V2_1/VmsMessageType.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+ $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+ -Ljava \
+ -randroid.hardware:hardware/interfaces \
+ -randroid.hidl:system/libhidl/transport \
+ android.hardware.automotive.vehicle@2.1::types.VmsMessageType
+
+$(GEN): $(LOCAL_PATH)/types.hal
+ $(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+#
# Build IVehicle.hal
#
GEN := $(intermediates)/android/hardware/automotive/vehicle/V2_1/IVehicle.java
diff --git a/automotive/vehicle/2.1/default/impl/vhal_v2_1/DefaultConfig.h b/automotive/vehicle/2.1/default/impl/vhal_v2_1/DefaultConfig.h
index 769ae6d..6bdec59 100644
--- a/automotive/vehicle/2.1/default/impl/vhal_v2_1/DefaultConfig.h
+++ b/automotive/vehicle/2.1/default/impl/vhal_v2_1/DefaultConfig.h
@@ -59,6 +59,12 @@
.prop = V2_0::toInt(V2_1::VehicleProperty::OBD2_FREEZE_FRAME_CLEAR),
.access = V2_0::VehiclePropertyAccess::WRITE,
.changeMode = V2_0::VehiclePropertyChangeMode::ON_CHANGE
+ },
+
+ {
+ .prop = V2_0::toInt(VehicleProperty::VEHICLE_MAP_SERVICE),
+ .access = V2_0::VehiclePropertyAccess::READ_WRITE,
+ .changeMode = V2_0::VehiclePropertyChangeMode::ON_CHANGE
}
};
diff --git a/automotive/vehicle/2.1/types.hal b/automotive/vehicle/2.1/types.hal
index f0dd95f..9b219b2 100644
--- a/automotive/vehicle/2.1/types.hal
+++ b/automotive/vehicle/2.1/types.hal
@@ -174,6 +174,26 @@
| VehiclePropertyGroup:SYSTEM
| VehiclePropertyType:COMPLEX
| VehicleArea:GLOBAL),
+
+ /*
+ * Vehicle Maps Service (VMS) message
+ *
+ * This property uses COMPLEX data to communicate vms messages.
+ *
+ * Its contents are to be interpreted as follows:
+ * the indices defined in VmsMessageIntegerValuesIndex are to be used to
+ * read from int32Values;
+ * bytes is a serialized VMS message as defined in the vms protocol
+ * which is opaque to the framework;
+ *
+ * @change_mode VehiclePropertyChangeMode:ON_CHANGE
+ * @access VehiclePropertyAccess:READ_WRITE
+ */
+ VEHICLE_MAP_SERVICE = (
+ 0x0C00
+ | VehiclePropertyGroup:SYSTEM
+ | VehiclePropertyType:COMPLEX
+ | VehicleArea:GLOBAL),
};
/* The status of a fuel system as described by the OBD2 specification. */
@@ -551,3 +571,35 @@
LAST_SYSTEM_INDEX = ENGINE_FUEL_RATE,
};
+
+/*
+ * This enum lists the types of supported VMS messages.
+ */
+enum VmsMessageType : int32_t {
+ /* A client subscribes to a layer. */
+ SUBSCRIBE = 1,
+
+ /* A client unsubscribes from a layer. */
+ UNSUBSCRIBE = 2,
+
+ /* A client publishes a data packet. */
+ DATA = 3,
+};
+
+/*
+ * This enum provides the canonical mapping for VMS properties that have an
+ * integer value.
+ */
+enum VmsMessageIntegerValuesIndex : int32_t {
+ /* The message type as enumerated by VmsMessageType enum. */
+ VMS_MESSAGE_TYPE = 0,
+
+ /* The layer ID as defined in the vms protocol. */
+ VMS_LAYER_ID = 1,
+
+ /* The version of the VMS layer. */
+ VMS_LAYER_VERSION = 2,
+
+ /* The number of bytes in the payload */
+ VMS_PAYLOAD_SIZE_BYTES = 3,
+};
diff --git a/benchmarks/Android.bp b/benchmarks/Android.bp
deleted file mode 100644
index ab0f308..0000000
--- a/benchmarks/Android.bp
+++ /dev/null
@@ -1,4 +0,0 @@
-// This is an autogenerated file, do not edit.
-subdirs = [
- "msgq/1.0",
-]
diff --git a/benchmarks/msgq/1.0/Android.bp b/benchmarks/msgq/1.0/Android.bp
deleted file mode 100644
index 7f8ea99..0000000
--- a/benchmarks/msgq/1.0/Android.bp
+++ /dev/null
@@ -1,59 +0,0 @@
-// This file is autogenerated by hidl-gen. Do not edit manually.
-
-filegroup {
- name: "android.hardware.benchmarks.msgq@1.0_hal",
- srcs: [
- "IBenchmarkMsgQ.hal",
- ],
-}
-
-genrule {
- name: "android.hardware.benchmarks.msgq@1.0_genc++",
- tools: ["hidl-gen"],
- cmd: "$(location hidl-gen) -o $(genDir) -Lc++ -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport android.hardware.benchmarks.msgq@1.0",
- srcs: [
- ":android.hardware.benchmarks.msgq@1.0_hal",
- ],
- out: [
- "android/hardware/benchmarks/msgq/1.0/BenchmarkMsgQAll.cpp",
- ],
-}
-
-genrule {
- name: "android.hardware.benchmarks.msgq@1.0_genc++_headers",
- tools: ["hidl-gen"],
- cmd: "$(location hidl-gen) -o $(genDir) -Lc++ -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport android.hardware.benchmarks.msgq@1.0",
- srcs: [
- ":android.hardware.benchmarks.msgq@1.0_hal",
- ],
- out: [
- "android/hardware/benchmarks/msgq/1.0/IBenchmarkMsgQ.h",
- "android/hardware/benchmarks/msgq/1.0/IHwBenchmarkMsgQ.h",
- "android/hardware/benchmarks/msgq/1.0/BnHwBenchmarkMsgQ.h",
- "android/hardware/benchmarks/msgq/1.0/BpHwBenchmarkMsgQ.h",
- "android/hardware/benchmarks/msgq/1.0/BsBenchmarkMsgQ.h",
- ],
-}
-
-cc_library_shared {
- name: "android.hardware.benchmarks.msgq@1.0",
- generated_sources: ["android.hardware.benchmarks.msgq@1.0_genc++"],
- generated_headers: ["android.hardware.benchmarks.msgq@1.0_genc++_headers"],
- export_generated_headers: ["android.hardware.benchmarks.msgq@1.0_genc++_headers"],
- shared_libs: [
- "libhidlbase",
- "libhidltransport",
- "libhwbinder",
- "liblog",
- "libutils",
- "libcutils",
- "android.hidl.base@1.0",
- ],
- export_shared_lib_headers: [
- "libhidlbase",
- "libhidltransport",
- "libhwbinder",
- "libutils",
- "android.hidl.base@1.0",
- ],
-}
diff --git a/bluetooth/1.0/default/async_fd_watcher.cc b/bluetooth/1.0/default/async_fd_watcher.cc
index 287d007..2f23a69 100644
--- a/bluetooth/1.0/default/async_fd_watcher.cc
+++ b/bluetooth/1.0/default/async_fd_watcher.cc
@@ -19,6 +19,7 @@
#include <algorithm>
#include <atomic>
#include <condition_variable>
+#include <map>
#include <mutex>
#include <thread>
#include <vector>
@@ -26,6 +27,8 @@
#include "sys/select.h"
#include "unistd.h"
+static const int INVALID_FD = -1;
+
namespace android {
namespace hardware {
namespace bluetooth {
@@ -36,8 +39,7 @@
// Add file descriptor and callback
{
std::unique_lock<std::mutex> guard(internal_mutex_);
- read_fd_ = file_descriptor;
- cb_ = on_read_fd_ready_callback;
+ watched_fds_[file_descriptor] = on_read_fd_ready_callback;
}
// Start the thread if not started yet
@@ -58,7 +60,7 @@
return 0;
}
-void AsyncFdWatcher::StopWatchingFileDescriptor() { stopThread(); }
+void AsyncFdWatcher::StopWatchingFileDescriptors() { stopThread(); }
AsyncFdWatcher::~AsyncFdWatcher() {}
@@ -90,8 +92,7 @@
{
std::unique_lock<std::mutex> guard(internal_mutex_);
- cb_ = nullptr;
- read_fd_ = -1;
+ watched_fds_.clear();
}
{
@@ -115,7 +116,11 @@
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(notification_listen_fd_, &read_fds);
- FD_SET(read_fd_, &read_fds);
+ int max_read_fd = INVALID_FD;
+ for (auto& it : watched_fds_) {
+ FD_SET(it.first, &read_fds);
+ max_read_fd = std::max(max_read_fd, it.first);
+ }
struct timeval timeout;
struct timeval* timeout_ptr = NULL;
@@ -126,7 +131,7 @@
}
// Wait until there is data available to read on some FD.
- int nfds = std::max(notification_listen_fd_, read_fd_);
+ int nfds = std::max(notification_listen_fd_, max_read_fd);
int retval = select(nfds + 1, &read_fds, NULL, NULL, timeout_ptr);
// There was some error.
@@ -153,10 +158,21 @@
continue;
}
- // Invoke the data ready callback if appropriate.
- if (FD_ISSET(read_fd_, &read_fds)) {
+ // Invoke the data ready callbacks if appropriate.
+ std::vector<decltype(watched_fds_)::value_type> saved_callbacks;
+ {
std::unique_lock<std::mutex> guard(internal_mutex_);
- if (cb_) cb_(read_fd_);
+ for (auto& it : watched_fds_) {
+ if (FD_ISSET(it.first, &read_fds)) {
+ saved_callbacks.push_back(it);
+ }
+ }
+ }
+
+ for (auto& it : saved_callbacks) {
+ if (it.second) {
+ it.second(it.first);
+ }
}
}
}
diff --git a/bluetooth/1.0/default/async_fd_watcher.h b/bluetooth/1.0/default/async_fd_watcher.h
index 3f7ff54..358cbc3 100644
--- a/bluetooth/1.0/default/async_fd_watcher.h
+++ b/bluetooth/1.0/default/async_fd_watcher.h
@@ -16,6 +16,7 @@
#pragma once
+#include <map>
#include <mutex>
#include <thread>
@@ -36,7 +37,7 @@
const ReadCallback& on_read_fd_ready_callback);
int ConfigureTimeout(const std::chrono::milliseconds timeout,
const TimeoutCallback& on_timeout_callback);
- void StopWatchingFileDescriptor();
+ void StopWatchingFileDescriptors();
private:
AsyncFdWatcher(const AsyncFdWatcher&) = delete;
@@ -52,10 +53,9 @@
std::mutex internal_mutex_;
std::mutex timeout_mutex_;
- int read_fd_;
+ std::map<int, ReadCallback> watched_fds_;
int notification_listen_fd_;
int notification_write_fd_;
- ReadCallback cb_;
TimeoutCallback timeout_cb_;
std::chrono::milliseconds timeout_ms_;
};
diff --git a/bluetooth/1.0/default/test/async_fd_watcher_unittest.cc b/bluetooth/1.0/default/test/async_fd_watcher_unittest.cc
index a7f5bda..b0c533c 100644
--- a/bluetooth/1.0/default/test/async_fd_watcher_unittest.cc
+++ b/bluetooth/1.0/default/test/async_fd_watcher_unittest.cc
@@ -14,6 +14,8 @@
// limitations under the License.
//
+#define LOG_TAG "async_fd_watcher_unittest"
+
#include "async_fd_watcher.h"
#include <gtest/gtest.h>
#include <cstdint>
@@ -122,8 +124,8 @@
}
void CleanUpServer() {
- async_fd_watcher_.StopWatchingFileDescriptor();
- conn_watcher_.StopWatchingFileDescriptor();
+ async_fd_watcher_.StopWatchingFileDescriptors();
+ conn_watcher_.StopWatchingFileDescriptors();
close(socket_fd_);
}
@@ -211,7 +213,7 @@
});
ConnectClient();
- conn_watcher.StopWatchingFileDescriptor();
+ conn_watcher.StopWatchingFileDescriptors();
close(socket_fd);
}
@@ -233,7 +235,7 @@
EXPECT_FALSE(timed_out);
sleep(1);
EXPECT_TRUE(timed_out);
- conn_watcher.StopWatchingFileDescriptor();
+ conn_watcher.StopWatchingFileDescriptors();
close(socket_fd);
}
@@ -265,10 +267,64 @@
sleep(1);
EXPECT_TRUE(timed_out);
EXPECT_TRUE(timed_out2);
- conn_watcher.StopWatchingFileDescriptor();
+ conn_watcher.StopWatchingFileDescriptors();
close(socket_fd);
}
+// Use a single AsyncFdWatcher to watch two file descriptors.
+TEST_F(AsyncFdWatcherSocketTest, WatchTwoFileDescriptors) {
+ int sockfd[2];
+ socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd);
+ bool cb1_called = false;
+ bool* cb1_called_ptr = &cb1_called;
+ bool cb2_called = false;
+ bool* cb2_called_ptr = &cb2_called;
+
+ AsyncFdWatcher watcher;
+ watcher.WatchFdForNonBlockingReads(sockfd[0], [cb1_called_ptr](int fd) {
+ char read_buf[1] = {0};
+ int n = TEMP_FAILURE_RETRY(read(fd, read_buf, sizeof(read_buf)));
+ ASSERT_TRUE(n == sizeof(read_buf));
+ ASSERT_TRUE(read_buf[0] == '1');
+ *cb1_called_ptr = true;
+ });
+
+ watcher.WatchFdForNonBlockingReads(sockfd[1], [cb2_called_ptr](int fd) {
+ char read_buf[1] = {0};
+ int n = TEMP_FAILURE_RETRY(read(fd, read_buf, sizeof(read_buf)));
+ ASSERT_TRUE(n == sizeof(read_buf));
+ ASSERT_TRUE(read_buf[0] == '2');
+ *cb2_called_ptr = true;
+ });
+
+ // Fail if the test doesn't pass within 3 seconds
+ watcher.ConfigureTimeout(std::chrono::seconds(3), [this]() {
+ bool connection_timeout = true;
+ ASSERT_FALSE(connection_timeout);
+ });
+
+ EXPECT_FALSE(cb1_called);
+ EXPECT_FALSE(cb2_called);
+
+ char one_buf[1] = {'1'};
+ TEMP_FAILURE_RETRY(write(sockfd[1], one_buf, sizeof(one_buf)));
+
+ sleep(1);
+
+ EXPECT_TRUE(cb1_called);
+ EXPECT_FALSE(cb2_called);
+
+ char two_buf[1] = {'2'};
+ TEMP_FAILURE_RETRY(write(sockfd[0], two_buf, sizeof(two_buf)));
+
+ sleep(1);
+
+ EXPECT_TRUE(cb1_called);
+ EXPECT_TRUE(cb2_called);
+
+ watcher.StopWatchingFileDescriptors();
+}
+
// Use two AsyncFdWatchers to set up a server socket.
TEST_F(AsyncFdWatcherSocketTest, ClientServer) {
ConfigureServer();
diff --git a/bluetooth/1.0/default/vendor_interface.cc b/bluetooth/1.0/default/vendor_interface.cc
index 3878129..2c55d1c 100644
--- a/bluetooth/1.0/default/vendor_interface.cc
+++ b/bluetooth/1.0/default/vendor_interface.cc
@@ -274,7 +274,7 @@
}
void VendorInterface::Close() {
- fd_watcher_.StopWatchingFileDescriptor();
+ fd_watcher_.StopWatchingFileDescriptors();
if (lib_interface_ != nullptr) {
bt_vendor_lpm_mode_t mode = BT_VND_LPM_DISABLE;
diff --git a/broadcastradio/1.0/vts/functional/VtsHalBroadcastradioV1_0TargetTest.cpp b/broadcastradio/1.0/vts/functional/VtsHalBroadcastradioV1_0TargetTest.cpp
index 4212729..5106e51 100644
--- a/broadcastradio/1.0/vts/functional/VtsHalBroadcastradioV1_0TargetTest.cpp
+++ b/broadcastradio/1.0/vts/functional/VtsHalBroadcastradioV1_0TargetTest.cpp
@@ -42,6 +42,7 @@
using ::android::hardware::broadcastradio::V1_0::Result;
using ::android::hardware::broadcastradio::V1_0::Class;
using ::android::hardware::broadcastradio::V1_0::Properties;
+using ::android::hardware::broadcastradio::V1_0::Band;
using ::android::hardware::broadcastradio::V1_0::BandConfig;
using ::android::hardware::broadcastradio::V1_0::Direction;
using ::android::hardware::broadcastradio::V1_0::ProgramInfo;
@@ -83,15 +84,15 @@
return Void();
}
- virtual Return<void> configChange(Result result, const BandConfig& config __unused) {
+ virtual Return<void> configChange(Result result, const BandConfig& config) {
ALOGI("%s result %d", __FUNCTION__, result);
- mParentTest->onResultCallback(result);
+ mParentTest->onConfigChangeCallback(result, config);
return Void();
}
- virtual Return<void> tuneComplete(Result result, const ProgramInfo& info __unused) {
+ virtual Return<void> tuneComplete(Result result, const ProgramInfo& info) {
ALOGI("%s result %d", __FUNCTION__, result);
- mParentTest->onResultCallback(result);
+ mParentTest->onTuneCompleteCallback(result, info);
return Void();
}
@@ -146,11 +147,22 @@
}
/**
- * Method called by MyCallback when a callback with status is received
+ * Method called by MyCallback when configChange() callback is received.
*/
- void onResultCallback(Result result) {
+ void onConfigChangeCallback(Result result, const BandConfig& config) {
Mutex::Autolock _l(mLock);
mResultCallbackData = result;
+ mBandConfigCallbackData = config;
+ onCallback_l();
+ }
+
+ /**
+ * Method called by MyCallback when tuneComplete() callback is received.
+ */
+ void onTuneCompleteCallback(Result result, const ProgramInfo& info) {
+ Mutex::Autolock _l(mLock);
+ mResultCallbackData = result;
+ mProgramInfoCallbackData = info;
onCallback_l();
}
@@ -209,6 +221,8 @@
bool mCallbackCalled;
bool mBoolCallbackData;
Result mResultCallbackData;
+ ProgramInfo mProgramInfoCallbackData;
+ BandConfig mBandConfigCallbackData;
bool mHwFailure;
};
@@ -219,6 +233,35 @@
virtual void TearDown() {}
};
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_0 {
+
+/**
+ * Compares two BandConfig objects for testing purposes.
+ */
+static bool operator==(const BandConfig& l, const BandConfig& r) {
+ if (l.type != r.type) return false;
+ if (l.antennaConnected != r.antennaConnected) return false;
+ if (l.lowerLimit != r.lowerLimit) return false;
+ if (l.upperLimit != r.upperLimit) return false;
+ if (l.spacings != r.spacings) return false;
+ if (l.type == Band::AM || l.type == Band::AM_HD) {
+ return l.ext.am == r.ext.am;
+ } else if (l.type == Band::FM || l.type == Band::FM_HD) {
+ return l.ext.fm == r.ext.fm;
+ } else {
+ // unsupported type
+ return false;
+ }
+}
+
+} // V1_0
+} // broadcastradio
+} // hardware
+} // android
+
bool BroadcastRadioHidlTest::getProperties()
{
if (mHalProperties.bands.size() == 0) {
@@ -305,6 +348,40 @@
}
/**
+ * Test IBroadcastRadio::openTuner() after ITuner disposal.
+ *
+ * Verifies that:
+ * - ITuner destruction gets propagated through HAL
+ * - the openTuner method works well when called for the second time
+ */
+TEST_F(BroadcastRadioHidlTest, ReopenTuner) {
+ EXPECT_TRUE(openTuner());
+ mTuner.clear();
+ EXPECT_TRUE(openTuner());
+}
+
+/**
+ * Test IBroadcastRadio::openTuner() method called twice.
+ *
+ * Verifies that:
+ * - the openTuner method fails when called for the second time without deleting previous
+ * ITuner instance
+ */
+TEST_F(BroadcastRadioHidlTest, OpenTunerTwice) {
+ EXPECT_TRUE(openTuner());
+
+ Result halResult = Result::NOT_INITIALIZED;
+ Return<void> hidlReturn =
+ mRadio->openTuner(mHalProperties.bands[0], true, mTunerCallback,
+ [&](Result result, const sp<ITuner>&) {
+ halResult = result;
+ });
+ EXPECT_TRUE(hidlReturn.isOk());
+ EXPECT_EQ(Result::INVALID_STATE, halResult);
+ EXPECT_TRUE(waitForCallback(kConfigCallbacktimeoutNs));
+}
+
+/**
* Test ITuner::setConfiguration() and getConfiguration methods
*
* Verifies that:
@@ -317,11 +394,12 @@
ASSERT_EQ(true, openTuner());
// test setConfiguration
mCallbackCalled = false;
- Return<Result> hidlResult = mTuner->setConfiguration(mHalProperties.bands[0]);
+ Return<Result> hidlResult = mTuner->setConfiguration(mHalProperties.bands[1]);
EXPECT_TRUE(hidlResult.isOk());
EXPECT_EQ(Result::OK, hidlResult);
EXPECT_EQ(true, waitForCallback(kConfigCallbacktimeoutNs));
EXPECT_EQ(Result::OK, mResultCallbackData);
+ EXPECT_EQ(mHalProperties.bands[1], mBandConfigCallbackData);
// test getConfiguration
BandConfig halConfig;
@@ -335,7 +413,7 @@
});
EXPECT_TRUE(hidlReturn.isOk());
EXPECT_EQ(Result::OK, halResult);
- EXPECT_EQ(mHalProperties.bands[0].type, halConfig.type);
+ EXPECT_EQ(mHalProperties.bands[1], halConfig);
}
/**
@@ -419,6 +497,7 @@
EXPECT_TRUE(hidlResult.isOk());
EXPECT_EQ(Result::OK, hidlResult);
EXPECT_EQ(true, waitForCallback(kTuneCallbacktimeoutNs));
+ EXPECT_EQ(channel, mProgramInfoCallbackData.channel);
// test getProgramInformation
ProgramInfo halInfo;
@@ -447,6 +526,42 @@
EXPECT_EQ(Result::OK, hidlResult);
}
+/**
+ * Test ITuner::tune failing when channel out of the range is provided.
+ *
+ * Verifies that:
+ * - the method returns INVALID_ARGUMENTS when applicable
+ * - the method recovers and succeeds after passing correct arguments
+ */
+TEST_F(BroadcastRadioHidlTest, TuneFailsOutOfBounds) {
+ ASSERT_TRUE(openTuner());
+ ASSERT_TRUE(checkAntenna());
+
+ // get current channel bounds
+ BandConfig halConfig;
+ Result halResult;
+ auto configResult = mTuner->getConfiguration([&](Result result, const BandConfig& config) {
+ halResult = result;
+ halConfig = config;
+ });
+ ASSERT_TRUE(configResult.isOk());
+ ASSERT_EQ(Result::OK, halResult);
+
+ // try to tune slightly above the limit and expect to fail
+ auto badChannel = halConfig.upperLimit + halConfig.spacings[0];
+ auto tuneResult = mTuner->tune(badChannel, 0);
+ EXPECT_TRUE(tuneResult.isOk());
+ EXPECT_EQ(Result::INVALID_ARGUMENTS, tuneResult);
+ EXPECT_TRUE(waitForCallback(kTuneCallbacktimeoutNs));
+
+ // tuning exactly at the limit should succeed
+ auto goodChannel = halConfig.upperLimit;
+ tuneResult = mTuner->tune(goodChannel, 0);
+ EXPECT_TRUE(tuneResult.isOk());
+ EXPECT_EQ(Result::OK, tuneResult);
+ EXPECT_TRUE(waitForCallback(kTuneCallbacktimeoutNs));
+}
+
int main(int argc, char** argv) {
::testing::AddGlobalTestEnvironment(new BroadcastRadioHidlEnvironment);
diff --git a/camera/Android.bp b/camera/Android.bp
index e379e49..3869766 100644
--- a/camera/Android.bp
+++ b/camera/Android.bp
@@ -3,6 +3,7 @@
"common/1.0",
"common/1.0/default",
"device/1.0",
+ "device/1.0/default",
"device/3.2",
"device/3.2/default",
"metadata/3.2",
diff --git a/camera/common/1.0/default/Android.bp b/camera/common/1.0/default/Android.bp
index af0ff6e..9ec266c 100644
--- a/camera/common/1.0/default/Android.bp
+++ b/camera/common/1.0/default/Android.bp
@@ -1,6 +1,10 @@
cc_library_static {
name: "android.hardware.camera.common@1.0-helper",
- srcs: ["CameraModule.cpp", "CameraMetadata.cpp", "VendorTagDescriptor.cpp"],
+ srcs: [
+ "CameraModule.cpp",
+ "CameraMetadata.cpp",
+ "VendorTagDescriptor.cpp",
+ "HandleImporter.cpp"],
cflags: [
"-Werror",
"-Wextra",
@@ -13,3 +17,4 @@
include_dirs: ["system/media/private/camera/include"],
export_include_dirs : ["include"]
}
+
diff --git a/camera/common/1.0/default/HandleImporter.cpp b/camera/common/1.0/default/HandleImporter.cpp
new file mode 100644
index 0000000..dee2973
--- /dev/null
+++ b/camera/common/1.0/default/HandleImporter.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "HandleImporter"
+#include <utils/Log.h>
+#include "HandleImporter.h"
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace common {
+namespace V1_0 {
+namespace helper {
+
+HandleImporter HandleImporter::sHandleImporter;
+
+HandleImporter& HandleImporter::getInstance() {
+ sHandleImporter.initialize();
+ return sHandleImporter;
+}
+
+bool HandleImporter::initialize() {
+ // allow only one client
+ if (mInitialized) {
+ return false;
+ }
+
+ if (!openGralloc()) {
+ return false;
+ }
+
+ mInitialized = true;
+ return true;
+}
+
+void HandleImporter::cleanup() {
+ if (!mInitialized) {
+ return;
+ }
+
+ closeGralloc();
+ mInitialized = false;
+}
+
+// In IComposer, any buffer_handle_t is owned by the caller and we need to
+// make a clone for hwcomposer2. We also need to translate empty handle
+// to nullptr. This function does that, in-place.
+bool HandleImporter::importBuffer(buffer_handle_t& handle) {
+ if (!handle->numFds && !handle->numInts) {
+ handle = nullptr;
+ return true;
+ }
+
+ buffer_handle_t clone = cloneBuffer(handle);
+ if (!clone) {
+ return false;
+ }
+
+ handle = clone;
+ return true;
+}
+
+void HandleImporter::freeBuffer(buffer_handle_t handle) {
+ if (!handle) {
+ return;
+ }
+
+ releaseBuffer(handle);
+}
+
+bool HandleImporter::importFence(const native_handle_t* handle, int& fd) {
+ if (handle == nullptr || handle->numFds == 0) {
+ fd = -1;
+ } else if (handle->numFds == 1) {
+ fd = dup(handle->data[0]);
+ if (fd < 0) {
+ ALOGE("failed to dup fence fd %d", handle->data[0]);
+ return false;
+ }
+ } else {
+ ALOGE("invalid fence handle with %d file descriptors",
+ handle->numFds);
+ return false;
+ }
+
+ return true;
+}
+
+void HandleImporter::closeFence(int fd) {
+ if (fd >= 0) {
+ close(fd);
+ }
+}
+
+bool HandleImporter::openGralloc() {
+ const hw_module_t* module;
+ int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
+ if (err) {
+ ALOGE("failed to get gralloc module");
+ return false;
+ }
+
+ uint8_t major = (module->module_api_version >> 8) & 0xff;
+ if (major > 1) {
+ ALOGE("unknown gralloc module major version %d", major);
+ return false;
+ }
+
+ if (major == 1) {
+ err = gralloc1_open(module, &mDevice);
+ if (err) {
+ ALOGE("failed to open gralloc1 device");
+ return false;
+ }
+
+ mRetain = reinterpret_cast<GRALLOC1_PFN_RETAIN>(
+ mDevice->getFunction(mDevice, GRALLOC1_FUNCTION_RETAIN));
+ mRelease = reinterpret_cast<GRALLOC1_PFN_RELEASE>(
+ mDevice->getFunction(mDevice, GRALLOC1_FUNCTION_RELEASE));
+ if (!mRetain || !mRelease) {
+ ALOGE("invalid gralloc1 device");
+ gralloc1_close(mDevice);
+ return false;
+ }
+ } else {
+ mModule = reinterpret_cast<const gralloc_module_t*>(module);
+ }
+
+ return true;
+}
+
+void HandleImporter::closeGralloc() {
+ if (mDevice) {
+ gralloc1_close(mDevice);
+ }
+}
+
+buffer_handle_t HandleImporter::cloneBuffer(buffer_handle_t handle) {
+ native_handle_t* clone = native_handle_clone(handle);
+ if (!clone) {
+ ALOGE("failed to clone buffer %p", handle);
+ return nullptr;
+ }
+
+ bool err;
+ if (mDevice) {
+ err = (mRetain(mDevice, clone) != GRALLOC1_ERROR_NONE);
+ } else {
+ err = (mModule->registerBuffer(mModule, clone) != 0);
+ }
+
+ if (err) {
+ ALOGE("failed to retain/register buffer %p", clone);
+ native_handle_close(clone);
+ native_handle_delete(clone);
+ return nullptr;
+ }
+
+ return clone;
+}
+
+void HandleImporter::releaseBuffer(buffer_handle_t handle) {
+ if (mDevice) {
+ mRelease(mDevice, handle);
+ } else {
+ mModule->unregisterBuffer(mModule, handle);
+ }
+ native_handle_close(handle);
+ native_handle_delete(const_cast<native_handle_t*>(handle));
+}
+
+} // namespace helper
+} // namespace V1_0
+} // namespace common
+} // namespace camera
+} // namespace hardware
+} // namespace android
diff --git a/camera/common/1.0/default/include/CameraModule.h b/camera/common/1.0/default/include/CameraModule.h
index 68d4f90..9fbfbd5 100644
--- a/camera/common/1.0/default/include/CameraModule.h
+++ b/camera/common/1.0/default/include/CameraModule.h
@@ -20,6 +20,7 @@
#include <hardware/camera.h>
#include <utils/Mutex.h>
#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
#include "CameraMetadata.h"
diff --git a/camera/common/1.0/default/include/HandleImporter.h b/camera/common/1.0/default/include/HandleImporter.h
new file mode 100644
index 0000000..def8982
--- /dev/null
+++ b/camera/common/1.0/default/include/HandleImporter.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef CAMERA_COMMON_1_0_HANDLEIMPORTED_H
+#define CAMERA_COMMON_1_0_HANDLEIMPORTED_H
+
+#include <hardware/gralloc.h>
+#include <hardware/gralloc1.h>
+#include <system/window.h>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace common {
+namespace V1_0 {
+namespace helper {
+
+// Borrowed from graphics HAL. Use this until gralloc mapper HAL is working
+class HandleImporter {
+public:
+ static HandleImporter& getInstance();
+
+ // In IComposer, any buffer_handle_t is owned by the caller and we need to
+ // make a clone for hwcomposer2. We also need to translate empty handle
+ // to nullptr. This function does that, in-place.
+ bool importBuffer(buffer_handle_t& handle);
+ void freeBuffer(buffer_handle_t handle);
+ bool importFence(const native_handle_t* handle, int& fd);
+ void closeFence(int fd);
+
+private:
+
+ HandleImporter() : mInitialized(false) {}
+ bool initialize();
+ void cleanup();
+ bool openGralloc();
+ void closeGralloc();
+ buffer_handle_t cloneBuffer(buffer_handle_t handle);
+ void releaseBuffer(buffer_handle_t handle);
+
+ static HandleImporter sHandleImporter;
+ bool mInitialized;
+
+ // gralloc1
+ gralloc1_device_t* mDevice;
+ GRALLOC1_PFN_RETAIN mRetain;
+ GRALLOC1_PFN_RELEASE mRelease;
+
+ // gralloc0
+ const gralloc_module_t* mModule;
+};
+
+} // namespace helper
+} // namespace V1_0
+} // namespace common
+} // namespace camera
+} // namespace hardware
+} // namespace android
+
+#endif // CAMERA_COMMON_1_0_HANDLEIMPORTED_H
\ No newline at end of file
diff --git a/camera/device/1.0/ICameraDevice.hal b/camera/device/1.0/ICameraDevice.hal
index d232a67..4a3a406 100644
--- a/camera/device/1.0/ICameraDevice.hal
+++ b/camera/device/1.0/ICameraDevice.hal
@@ -298,7 +298,8 @@
recordingEnabled() generates (bool enabled);
/**
- * Release a record frame previously returned by CAMERA_MSG_VIDEO_FRAME.
+ * Release a record frame previously returned by CAMERA_MSG_VIDEO_FRAME in
+ * dataCallbackTimestamp.
*
* It is camera HAL client's responsibility to release video recording
* frames sent out by the camera HAL before the camera HAL receives a call
@@ -306,10 +307,26 @@
* disableMsgType(CAMERA_MSG_VIDEO_FRAME), it is the camera HAL's
* responsibility to manage the life-cycle of the video recording frames.
*
- * @param data The memory buffer to release a recording frame from.
+ * @param memId The memory buffer to release a recording frame from.
* @param bufferIndex The specific buffer index to return to the HAL.
*/
- releaseRecordingFrame(MemoryId data, uint32_t bufferIndex);
+ releaseRecordingFrame(MemoryId memId, uint32_t bufferIndex);
+
+ /**
+ * Release a record frame previously returned by CAMERA_MSG_VIDEO_FRAME in
+ * handleCallbackTimestamp.
+ *
+ * It is camera HAL client's responsibility to release video recording
+ * frames sent out by the camera HAL before the camera HAL receives a call
+ * to disableMsgType(CAMERA_MSG_VIDEO_FRAME). After it receives the call to
+ * disableMsgType(CAMERA_MSG_VIDEO_FRAME), it is the camera HAL's
+ * responsibility to manage the life-cycle of the video recording frames.
+ *
+ * @param memId The memory buffer to release a recording frame from.
+ * @param bufferIndex The specific buffer index to return to the HAL.
+ * @param frame The handle for a released video frame
+ */
+ releaseRecordingFrameHandle(MemoryId memId, uint32_t bufferIndex, handle frame);
/**
* Start auto focus.
@@ -352,14 +369,14 @@
/**
* Set the camera parameters.
*
- * @param parms The parameter string, consisting of
+ * @param params The parameter string, consisting of
* '<key1>=<value1>; ...;<keyN>=<valueN>'.
* @return status The status code for this operation:
* OK: Parameter update was successful
* ILLEGAL_ARGUMENT: At least one parameter was invalid or not supported
*
*/
- setParameters(string parms) generates (Status status);
+ setParameters(string params) generates (Status status);
/**
* Retrieve the camera parameters.
diff --git a/camera/device/1.0/ICameraDeviceCallback.hal b/camera/device/1.0/ICameraDeviceCallback.hal
index 97014ee..1b0db24 100644
--- a/camera/device/1.0/ICameraDeviceCallback.hal
+++ b/camera/device/1.0/ICameraDeviceCallback.hal
@@ -42,8 +42,10 @@
* @param bufferCount The number of contiguous buffers that the descriptor
* contains.
*
- * @return memId A integer identifier for this memory buffer, for use with
- * data callbacks and unregistering memory.
+ * @return memId A positive integer identifier for this memory buffer, for
+ * use with data callbacks and unregistering memory. 0 must be returned
+ * in case of error, such as if the descriptor does not contain exactly
+ * one FD.
*/
registerMemory(handle descriptor, uint32_t bufferSize, uint32_t bufferCount)
generates (MemoryId memId);
@@ -62,7 +64,8 @@
* starts.
*
*/
- dataCallback(DataCallbackMsg msgType, MemoryId data, uint32_t bufferIndex);
+ dataCallback(DataCallbackMsg msgType, MemoryId data, uint32_t bufferIndex,
+ CameraFrameMetadata metadata);
/**
* Send a buffer of image data to the camera service, with a timestamp
@@ -78,4 +81,21 @@
dataCallbackTimestamp(DataCallbackMsg msgType, MemoryId data, uint32_t bufferIndex,
int64_t timestamp);
+ /**
+ * Send a buffer of image data to the camera service, with a timestamp
+ *
+ * @param msgType The kind of image buffer data this call represents.
+ * @param handle The handle of image buffer data this call represents.
+ * @param data A memory handle to the buffer containing the data.
+ * @param bufferIndex The offset into the memory handle where the buffer
+ * starts.
+ * @param timestamp The time this buffer was captured by the camera, in
+ * nanoseconds.
+ *
+ * @return frameId a frame ID to be used with releaseRecordingFrameId later
+ *
+ */
+ handleCallbackTimestamp(DataCallbackMsg msgType, handle frameData, MemoryId data,
+ uint32_t bufferIndex, int64_t timestamp);
+
};
diff --git a/camera/device/1.0/ICameraDevicePreviewCallback.hal b/camera/device/1.0/ICameraDevicePreviewCallback.hal
index ebc7460..4c9b517 100644
--- a/camera/device/1.0/ICameraDevicePreviewCallback.hal
+++ b/camera/device/1.0/ICameraDevicePreviewCallback.hal
@@ -30,28 +30,31 @@
*
* @return status The status code for this operation. If not OK, then
* buffer and stride must not be used.
- * @return buffer A handle to the buffer to write into.
+ * @return bufferId A unique ID for the returned buffer.
+ * @return buffer A handle to the buffer to write into. Must be non-null if the bufferId has not
+ * been seen by HAL before. Must be null if the bufferId is seen before. HAL must keep track
+ * of the bufferId to actual buffer handle mapping.
* @return stride The stride between two rows of pixels in this buffer.
*/
- dequeueBuffer() generates (Status status, handle buffer, uint32_t stride);
+ dequeueBuffer() generates (Status status, uint64_t bufferId, handle buffer, uint32_t stride);
/**
* Send a filled preview buffer to its consumer.
*
- * @param buffer The handle to the preview buffer that's been filled.
+ * @param bufferId The bufferId of the preview buffer
* @return status The status code for this operation.
*/
- enqueueBuffer(handle buffer) generates (Status status);
+ enqueueBuffer(uint64_t bufferId) generates (Status status);
/**
* Return a preview buffer unfilled. This buffer must not be sent on to the
* preview consumer as a valid buffer, but may be reused as if it were
* empty.
*
- * @param buffer The handle to the preview buffer to return.
+ * @param bufferId The bufferId of the preview buffer
* @return status The status code for this operation.
*/
- cancelBuffer(handle buffer) generates (Status status);
+ cancelBuffer(uint64_t bufferId) generates (Status status);
/**
* Set the number of preview buffers needed by the HAL.
diff --git a/camera/device/1.0/default/Android.bp b/camera/device/1.0/default/Android.bp
new file mode 100644
index 0000000..1663787
--- /dev/null
+++ b/camera/device/1.0/default/Android.bp
@@ -0,0 +1,27 @@
+cc_library_shared {
+ name: "camera.device@1.0-impl",
+ srcs: [
+ "CameraDevice.cpp",
+ ],
+ shared_libs: [
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "libutils",
+ "android.hardware.camera.device@1.0",
+ "android.hardware.camera.common@1.0",
+ "android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.common@1.0",
+ "android.hidl.base@1.0",
+ "libcutils",
+ "liblog",
+ "libhardware",
+ "libcamera_metadata",
+ "libbinder",
+ ],
+ static_libs: [
+ "android.hardware.camera.common@1.0-helper"
+ ],
+ export_include_dirs: ["."]
+}
+
diff --git a/camera/device/1.0/default/CameraDevice.cpp b/camera/device/1.0/default/CameraDevice.cpp
new file mode 100644
index 0000000..819525b
--- /dev/null
+++ b/camera/device/1.0/default/CameraDevice.cpp
@@ -0,0 +1,948 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "CamDev@1.0-impl"
+#include <utils/Log.h>
+#include <hardware/camera.h>
+#include <hardware/gralloc1.h>
+#include <utils/Trace.h>
+
+#include "CameraDevice_1_0.h"
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::graphics::allocator::V2_0::ProducerUsage;
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+
+HandleImporter& CameraDevice::sHandleImporter = HandleImporter::getInstance();
+
+Status CameraDevice::getHidlStatus(const int& status) {
+ switch (status) {
+ case 0: return Status::OK;
+ case -ENOSYS: return Status::OPERATION_NOT_SUPPORTED;
+ case -EBUSY : return Status::CAMERA_IN_USE;
+ case -EUSERS: return Status::MAX_CAMERAS_IN_USE;
+ case -ENODEV: return Status::INTERNAL_ERROR;
+ case -EINVAL: return Status::ILLEGAL_ARGUMENT;
+ default:
+ ALOGE("%s: unknown HAL status code %d", __FUNCTION__, status);
+ return Status::INTERNAL_ERROR;
+ }
+}
+
+status_t CameraDevice::getStatusT(const Status& s) {
+ switch(s) {
+ case Status::OK:
+ return OK;
+ case Status::ILLEGAL_ARGUMENT:
+ return BAD_VALUE;
+ case Status::CAMERA_IN_USE:
+ return -EBUSY;
+ case Status::MAX_CAMERAS_IN_USE:
+ return -EUSERS;
+ case Status::METHOD_NOT_SUPPORTED:
+ return UNKNOWN_TRANSACTION;
+ case Status::OPERATION_NOT_SUPPORTED:
+ return INVALID_OPERATION;
+ case Status::CAMERA_DISCONNECTED:
+ return DEAD_OBJECT;
+ case Status::INTERNAL_ERROR:
+ return INVALID_OPERATION;
+ }
+ ALOGW("Unexpected HAL status code %d", s);
+ return INVALID_OPERATION;
+}
+
+Status CameraDevice::initStatus() const {
+ Mutex::Autolock _l(mLock);
+ Status status = Status::OK;
+ if (mInitFail) {
+ status = Status::INTERNAL_ERROR;
+ } else if (mDisconnected) {
+ status = Status::CAMERA_DISCONNECTED;
+ }
+ return status;
+}
+
+CameraDevice::CameraDevice(
+ sp<CameraModule> module, const std::string& cameraId,
+ const SortedVector<std::pair<std::string, std::string>>& cameraDeviceNames) :
+ mModule(module),
+ mCameraId(cameraId),
+ mDisconnected(false),
+ mCameraDeviceNames(cameraDeviceNames) {
+ mCameraIdInt = atoi(mCameraId.c_str());
+ // Should not reach here as provider also validate ID
+ if (mCameraIdInt < 0 || mCameraIdInt >= module->getNumberOfCameras()) {
+ ALOGE("%s: Invalid camera id: %s", __FUNCTION__, mCameraId.c_str());
+ mInitFail = true;
+ }
+
+ mDeviceVersion = mModule->getDeviceVersion(mCameraIdInt);
+ if (mDeviceVersion != CAMERA_DEVICE_API_VERSION_1_0 && !mModule->isOpenLegacyDefined()) {
+ ALOGI("%s: Camera id %s does not support HAL1.0",
+ __FUNCTION__, mCameraId.c_str());
+ mInitFail = true;
+ }
+}
+
+CameraDevice::~CameraDevice() {
+ Mutex::Autolock _l(mLock);
+ if (mDevice != nullptr) {
+ ALOGW("%s: camera %s is deleted while open", __FUNCTION__, mCameraId.c_str());
+ close();
+ }
+ mHalPreviewWindow.cleanUpCirculatingBuffers();
+}
+
+
+void CameraDevice::setConnectionStatus(bool connected) {
+ Mutex::Autolock _l(mLock);
+ mDisconnected = !connected;
+ if (mDevice == nullptr) {
+ return;
+ }
+ if (!connected) {
+ ALOGW("%s: camera %s is disconneted. Closing", __FUNCTION__, mCameraId.c_str());
+ close();
+ }
+ return;
+}
+
+void CameraDevice::CameraPreviewWindow::cleanUpCirculatingBuffers() {
+ Mutex::Autolock _l(mLock);
+ for (auto pair : mCirculatingBuffers) {
+ sHandleImporter.freeBuffer(pair.second);
+ }
+ mCirculatingBuffers.clear();
+ mBufferIdMap.clear();
+}
+
+int CameraDevice::sDequeueBuffer(struct preview_stream_ops* w,
+ buffer_handle_t** buffer, int *stride) {
+ CameraPreviewWindow* object = static_cast<CameraPreviewWindow*>(w);
+ if (object->mPreviewCallback == nullptr) {
+ ALOGE("%s: camera HAL calling preview ops while there is no preview window!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ if (buffer == nullptr || stride == nullptr) {
+ ALOGE("%s: buffer (%p) and stride (%p) must not be null!", __FUNCTION__, buffer, stride);
+ return BAD_VALUE;
+ }
+
+ Status s;
+ object->mPreviewCallback->dequeueBuffer(
+ [&](auto status, uint64_t bufferId, const auto& buf, uint32_t strd) {
+ s = status;
+ if (s == Status::OK) {
+ Mutex::Autolock _l(object->mLock);
+ if (object->mCirculatingBuffers.count(bufferId) == 0) {
+ buffer_handle_t importedBuf = buf.getNativeHandle();
+ sHandleImporter.importBuffer(importedBuf);
+ if (importedBuf == nullptr) {
+ ALOGE("%s: preview buffer import failed!", __FUNCTION__);
+ s = Status::INTERNAL_ERROR;
+ return;
+ } else {
+ object->mCirculatingBuffers[bufferId] = importedBuf;
+ object->mBufferIdMap[&(object->mCirculatingBuffers[bufferId])] = bufferId;
+ }
+ }
+ *buffer = &(object->mCirculatingBuffers[bufferId]);
+ *stride = strd;
+ }
+ });
+ return getStatusT(s);
+}
+
+int CameraDevice::sLockBuffer(struct preview_stream_ops*, buffer_handle_t*) {
+ // TODO: make sure lock_buffer is indeed a no-op (and will always be)
+ return 0;
+}
+
+int CameraDevice::sEnqueueBuffer(struct preview_stream_ops* w, buffer_handle_t* buffer) {
+ CameraPreviewWindow* object = static_cast<CameraPreviewWindow*>(w);
+ if (object->mPreviewCallback == nullptr) {
+ ALOGE("%s: camera HAL calling preview ops while there is no preview window!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ uint64_t bufferId = object->mBufferIdMap.at(buffer);
+ return getStatusT(object->mPreviewCallback->enqueueBuffer(bufferId));
+}
+
+int CameraDevice::sCancelBuffer(struct preview_stream_ops* w, buffer_handle_t* buffer) {
+ CameraPreviewWindow* object = static_cast<CameraPreviewWindow*>(w);
+ if (object->mPreviewCallback == nullptr) {
+ ALOGE("%s: camera HAL calling preview ops while there is no preview window!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ uint64_t bufferId = object->mBufferIdMap.at(buffer);
+ return getStatusT(object->mPreviewCallback->cancelBuffer(bufferId));
+}
+
+int CameraDevice::sSetBufferCount(struct preview_stream_ops* w, int count) {
+ CameraPreviewWindow* object = static_cast<CameraPreviewWindow*>(w);
+ if (object->mPreviewCallback == nullptr) {
+ ALOGE("%s: camera HAL calling preview ops while there is no preview window!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ object->cleanUpCirculatingBuffers();
+ return getStatusT(object->mPreviewCallback->setBufferCount(count));
+}
+
+int CameraDevice::sSetBuffersGeometry(struct preview_stream_ops* w,
+ int width, int height, int format) {
+ CameraPreviewWindow* object = static_cast<CameraPreviewWindow*>(w);
+ if (object->mPreviewCallback == nullptr) {
+ ALOGE("%s: camera HAL calling preview ops while there is no preview window!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ object->cleanUpCirculatingBuffers();
+ return getStatusT(
+ object->mPreviewCallback->setBuffersGeometry(width, height, (PixelFormat) format));
+}
+
+int CameraDevice::sSetCrop(struct preview_stream_ops *w,
+ int left, int top, int right, int bottom) {
+ CameraPreviewWindow* object = static_cast<CameraPreviewWindow*>(w);
+ if (object->mPreviewCallback == nullptr) {
+ ALOGE("%s: camera HAL calling preview ops while there is no preview window!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ return getStatusT(object->mPreviewCallback->setCrop(left, top, right, bottom));
+}
+
+int CameraDevice::sSetTimestamp(struct preview_stream_ops *w, int64_t timestamp) {
+ CameraPreviewWindow* object = static_cast<CameraPreviewWindow*>(w);
+ if (object->mPreviewCallback == nullptr) {
+ ALOGE("%s: camera HAL calling preview ops while there is no preview window!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ return getStatusT(object->mPreviewCallback->setTimestamp(timestamp));
+}
+
+int CameraDevice::sSetUsage(struct preview_stream_ops* w, int usage) {
+ CameraPreviewWindow* object = static_cast<CameraPreviewWindow*>(w);
+ if (object->mPreviewCallback == nullptr) {
+ ALOGE("%s: camera HAL calling preview ops while there is no preview window!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ object->cleanUpCirculatingBuffers();
+ return getStatusT(object->mPreviewCallback->setUsage((ProducerUsage) usage));
+}
+
+int CameraDevice::sSetSwapInterval(struct preview_stream_ops *w, int interval) {
+ CameraPreviewWindow* object = static_cast<CameraPreviewWindow*>(w);
+ if (object->mPreviewCallback == nullptr) {
+ ALOGE("%s: camera HAL calling preview ops while there is no preview window!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ return getStatusT(object->mPreviewCallback->setSwapInterval(interval));
+}
+
+int CameraDevice::sGetMinUndequeuedBufferCount(
+ const struct preview_stream_ops *w,
+ int *count) {
+ const CameraPreviewWindow* object = static_cast<const CameraPreviewWindow*>(w);
+ if (object->mPreviewCallback == nullptr) {
+ ALOGE("%s: camera HAL calling preview ops while there is no preview window!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ if (count == nullptr) {
+ ALOGE("%s: count is null!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ Status s;
+ object->mPreviewCallback->getMinUndequeuedBufferCount(
+ [&](auto status, uint32_t cnt) {
+ s = status;
+ if (s == Status::OK) {
+ *count = cnt;
+ }
+ });
+ return getStatusT(s);
+}
+
+CameraDevice::CameraHeapMemory::CameraHeapMemory(int fd, size_t buf_size, uint_t num_buffers) :
+ mBufSize(buf_size),
+ mNumBufs(num_buffers) {
+ mHeap = new MemoryHeapBase(fd, buf_size * num_buffers);
+ commonInitialization();
+}
+
+CameraDevice::CameraHeapMemory::CameraHeapMemory(size_t buf_size, uint_t num_buffers) :
+ mBufSize(buf_size),
+ mNumBufs(num_buffers) {
+ mHeap = new MemoryHeapBase(buf_size * num_buffers);
+ commonInitialization();
+}
+
+void CameraDevice::CameraHeapMemory::commonInitialization() {
+ handle.data = mHeap->base();
+ handle.size = mBufSize * mNumBufs;
+ handle.handle = this;
+
+ mBuffers = new sp<MemoryBase>[mNumBufs];
+ for (uint_t i = 0; i < mNumBufs; i++) {
+ mBuffers[i] = new MemoryBase(mHeap, i * mBufSize, mBufSize);
+ }
+
+ handle.release = sPutMemory;
+}
+
+CameraDevice::CameraHeapMemory::~CameraHeapMemory() {
+ delete [] mBuffers;
+}
+
+// shared memory methods
+camera_memory_t* CameraDevice::sGetMemory(int fd, size_t buf_size, uint_t num_bufs, void *user) {
+ ALOGV("%s", __FUNCTION__);
+ CameraDevice* object = static_cast<CameraDevice*>(user);
+ if (object->mDeviceCallback == nullptr) {
+ ALOGE("%s: camera HAL request memory while camera is not opened!", __FUNCTION__);
+ return nullptr;
+ }
+
+ CameraHeapMemory* mem;
+ native_handle_t* handle = native_handle_create(1,0);
+
+ if (handle == nullptr) {
+ ALOGE("%s: native_handle_create failed!", __FUNCTION__);
+ return nullptr;
+ }
+
+ if (fd < 0) {
+ mem = new CameraHeapMemory(buf_size, num_bufs);
+ } else {
+ mem = new CameraHeapMemory(fd, buf_size, num_bufs);
+ }
+ handle->data[0] = mem->mHeap->getHeapID();
+ mem->incStrong(mem);
+
+ hidl_handle hidlHandle = handle;
+ MemoryId id = object->mDeviceCallback->registerMemory(hidlHandle, buf_size, num_bufs);
+ mem->handle.mId = id;
+ if (object->mMemoryMap.count(id) != 0) {
+ ALOGE("%s: duplicate MemoryId %d returned by client!", __FUNCTION__, id);
+ }
+ object->mMemoryMap[id] = mem;
+ mem->handle.mDevice = object;
+ native_handle_delete(handle);
+ return &mem->handle;
+}
+
+void CameraDevice::sPutMemory(camera_memory_t *data) {
+ if (!data)
+ return;
+
+ CameraHeapMemory* mem = static_cast<CameraHeapMemory *>(data->handle);
+ CameraDevice* device = mem->handle.mDevice;
+ if (device == nullptr) {
+ ALOGE("%s: camera HAL return memory for a null device!", __FUNCTION__);
+ }
+ if (device->mDeviceCallback == nullptr) {
+ ALOGE("%s: camera HAL return memory while camera is not opened!", __FUNCTION__);
+ }
+ device->mDeviceCallback->unregisterMemory(mem->handle.mId);
+ device->mMemoryMap.erase(mem->handle.mId);
+ mem->decStrong(mem);
+}
+
+// Callback forwarding methods
+void CameraDevice::sNotifyCb(int32_t msg_type, int32_t ext1, int32_t ext2, void *user) {
+ ALOGV("%s", __FUNCTION__);
+ CameraDevice* object = static_cast<CameraDevice*>(user);
+ if (object->mDeviceCallback != nullptr) {
+ object->mDeviceCallback->notifyCallback((NotifyCallbackMsg) msg_type, ext1, ext2);
+ }
+}
+
+void CameraDevice::sDataCb(int32_t msg_type, const camera_memory_t *data, unsigned int index,
+ camera_frame_metadata_t *metadata, void *user) {
+ ALOGV("%s", __FUNCTION__);
+ CameraDevice* object = static_cast<CameraDevice*>(user);
+ sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory*>(data->handle));
+ if (index >= mem->mNumBufs) {
+ ALOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__,
+ index, mem->mNumBufs);
+ return;
+ }
+ if (object->mDeviceCallback != nullptr) {
+ CameraFrameMetadata hidlMetadata;
+ if (metadata) {
+ hidlMetadata.faces.resize(metadata->number_of_faces);
+ for (size_t i = 0; i < hidlMetadata.faces.size(); i++) {
+ hidlMetadata.faces[i].score = metadata->faces[i].score;
+ hidlMetadata.faces[i].id = metadata->faces[i].id;
+ for (int k = 0; k < 4; k++) {
+ hidlMetadata.faces[i].rect[k] = metadata->faces[i].rect[k];
+ }
+ for (int k = 0; k < 2; k++) {
+ hidlMetadata.faces[i].leftEye[k] = metadata->faces[i].left_eye[k];
+ }
+ for (int k = 0; k < 2; k++) {
+ hidlMetadata.faces[i].rightEye[k] = metadata->faces[i].right_eye[k];
+ }
+ for (int k = 0; k < 2; k++) {
+ hidlMetadata.faces[i].mouth[k] = metadata->faces[i].mouth[k];
+ }
+ }
+ }
+ CameraHeapMemory* mem = static_cast<CameraHeapMemory *>(data->handle);
+ object->mDeviceCallback->dataCallback(
+ (DataCallbackMsg) msg_type, mem->handle.mId, index, hidlMetadata);
+ }
+}
+
+void CameraDevice::sDataCbTimestamp(nsecs_t timestamp, int32_t msg_type,
+ const camera_memory_t *data, unsigned index, void *user) {
+ ALOGV("%s", __FUNCTION__);
+ CameraDevice* object = static_cast<CameraDevice*>(user);
+ // Start refcounting the heap object from here on. When the clients
+ // drop all references, it will be destroyed (as well as the enclosed
+ // MemoryHeapBase.
+ sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory*>(data->handle));
+ if (index >= mem->mNumBufs) {
+ ALOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__,
+ index, mem->mNumBufs);
+ return;
+ }
+
+ native_handle_t* handle = nullptr;
+ if (object->mMetadataMode) {
+ if (mem->mBufSize == sizeof(VideoNativeHandleMetadata)) {
+ VideoNativeHandleMetadata* md = (VideoNativeHandleMetadata*)
+ mem->mBuffers[index]->pointer();
+ if (md->eType == VideoNativeHandleMetadata::kMetadataBufferTypeNativeHandleSource) {
+ handle = md->pHandle;
+ }
+ }
+ }
+
+ if (object->mDeviceCallback != nullptr) {
+ if (handle == nullptr) {
+ object->mDeviceCallback->dataCallbackTimestamp(
+ (DataCallbackMsg) msg_type, mem->handle.mId, index, timestamp);
+ } else {
+ object->mDeviceCallback->handleCallbackTimestamp(
+ (DataCallbackMsg) msg_type, handle, mem->handle.mId, index, timestamp);
+ }
+ }
+}
+
+void CameraDevice::initHalPreviewWindow()
+{
+ mHalPreviewWindow.cancel_buffer = sCancelBuffer;
+ mHalPreviewWindow.lock_buffer = sLockBuffer;
+ mHalPreviewWindow.dequeue_buffer = sDequeueBuffer;
+ mHalPreviewWindow.enqueue_buffer = sEnqueueBuffer;
+ mHalPreviewWindow.set_buffer_count = sSetBufferCount;
+ mHalPreviewWindow.set_buffers_geometry = sSetBuffersGeometry;
+ mHalPreviewWindow.set_crop = sSetCrop;
+ mHalPreviewWindow.set_timestamp = sSetTimestamp;
+ mHalPreviewWindow.set_usage = sSetUsage;
+ mHalPreviewWindow.set_swap_interval = sSetSwapInterval;
+
+ mHalPreviewWindow.get_min_undequeued_buffer_count =
+ sGetMinUndequeuedBufferCount;
+}
+
+// Methods from ::android::hardware::camera::device::V1_0::ICameraDevice follow.
+Return<void> CameraDevice::getResourceCost(getResourceCost_cb _hidl_cb) {
+ Status status = initStatus();
+ CameraResourceCost resCost;
+ if (status == Status::OK) {
+ int cost = 100;
+ std::vector<std::string> conflicting_devices;
+ struct camera_info info;
+
+ // If using post-2.4 module version, query the cost + conflicting devices from the HAL
+ if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4) {
+ int ret = mModule->getCameraInfo(mCameraIdInt, &info);
+ if (ret == OK) {
+ cost = info.resource_cost;
+ for (size_t i = 0; i < info.conflicting_devices_length; i++) {
+ std::string cameraId(info.conflicting_devices[i]);
+ for (const auto& pair : mCameraDeviceNames) {
+ if (cameraId == pair.first) {
+ conflicting_devices.push_back(pair.second);
+ }
+ }
+ }
+ } else {
+ status = Status::INTERNAL_ERROR;
+ }
+ }
+
+ if (status == Status::OK) {
+ resCost.resourceCost = cost;
+ resCost.conflictingDevices.resize(conflicting_devices.size());
+ for (size_t i = 0; i < conflicting_devices.size(); i++) {
+ resCost.conflictingDevices[i] = conflicting_devices[i];
+ ALOGV("CamDevice %s is conflicting with camDevice %s",
+ mCameraId.c_str(), resCost.conflictingDevices[i].c_str());
+ }
+ }
+ }
+ _hidl_cb(status, resCost);
+ return Void();
+}
+
+Return<void> CameraDevice::getCameraInfo(getCameraInfo_cb _hidl_cb) {
+ Status status = initStatus();
+ CameraInfo cameraInfo;
+ if (status == Status::OK) {
+ struct camera_info info;
+ int ret = mModule->getCameraInfo(mCameraIdInt, &info);
+ if (ret == OK) {
+ cameraInfo.facing = (CameraFacing) info.facing;
+ // Device 1.0 does not support external camera facing.
+ // The closest approximation would be front camera.
+ // TODO: figure out should we override here or let
+ // camera service handle it.
+ if (cameraInfo.facing == CameraFacing::EXTERNAL) {
+ cameraInfo.facing = CameraFacing::FRONT;
+ }
+ cameraInfo.orientation = info.orientation;
+ } else {
+ ALOGE("%s: get camera info failed!", __FUNCTION__);
+ status = Status::INTERNAL_ERROR;
+ }
+ }
+ _hidl_cb(status, cameraInfo);
+ return Void();
+}
+
+Return<Status> CameraDevice::setTorchMode(TorchMode mode) {
+ if (!mModule->isSetTorchModeSupported()) {
+ return Status::METHOD_NOT_SUPPORTED;
+ }
+
+ Status status = initStatus();
+ if (status == Status::OK) {
+ bool enable = (mode == TorchMode::ON) ? true : false;
+ status = getHidlStatus(mModule->setTorchMode(mCameraId.c_str(), enable));
+ }
+ return status;
+}
+
+Return<Status> CameraDevice::dumpState(const hidl_handle& handle) {
+ Mutex::Autolock _l(mLock);
+ if (handle.getNativeHandle() == nullptr) {
+ ALOGE("%s: handle must not be null", __FUNCTION__);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ if (handle->numFds != 1 || handle->numInts != 0) {
+ ALOGE("%s: handle must contain 1 FD and 0 integers! Got %d FDs and %d ints",
+ __FUNCTION__, handle->numFds, handle->numInts);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ int fd = handle->data[0];
+
+ if (mDevice != nullptr) {
+ if (mDevice->ops->dump) { // It's fine if the HAL doesn't implement dump()
+ return getHidlStatus(mDevice->ops->dump(mDevice, fd));
+ }
+ }
+ return Status::OK;
+}
+
+Return<Status> CameraDevice::open(const sp<ICameraDeviceCallback>& callback) {
+ ALOGI("Opening camera %s", mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+
+ camera_info info;
+ status_t res = mModule->getCameraInfo(mCameraIdInt, &info);
+ if (res != OK) {
+ ALOGE("Could not get camera info: %s: %d", mCameraId.c_str(), res);
+ return getHidlStatus(res);
+ }
+
+ int rc = OK;
+ if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_3 &&
+ info.device_version > CAMERA_DEVICE_API_VERSION_1_0) {
+ // Open higher version camera device as HAL1.0 device.
+ rc = mModule->openLegacy(mCameraId.c_str(),
+ CAMERA_DEVICE_API_VERSION_1_0,
+ (hw_device_t **)&mDevice);
+ } else {
+ rc = mModule->open(mCameraId.c_str(), (hw_device_t **)&mDevice);
+ }
+ if (rc != OK) {
+ mDevice = nullptr;
+ ALOGE("Could not open camera %s: %d", mCameraId.c_str(), rc);
+ return getHidlStatus(rc);
+ }
+
+ initHalPreviewWindow();
+ mDeviceCallback = callback;
+
+ if (mDevice->ops->set_callbacks) {
+ mDevice->ops->set_callbacks(mDevice,
+ sNotifyCb, sDataCb, sDataCbTimestamp, sGetMemory, this);
+ }
+
+ return getHidlStatus(rc);
+}
+
+Return<Status> CameraDevice::setPreviewWindow(const sp<ICameraDevicePreviewCallback>& window) {
+ ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ if (!mDevice) {
+ ALOGE("%s called while camera is not opened", __FUNCTION__);
+ return Status::OPERATION_NOT_SUPPORTED;
+ }
+
+ mHalPreviewWindow.mPreviewCallback = window;
+ if (mDevice->ops->set_preview_window) {
+ return getHidlStatus(mDevice->ops->set_preview_window(mDevice,
+ (window == nullptr) ? nullptr : &mHalPreviewWindow));
+ }
+ return Status::INTERNAL_ERROR; // HAL should provide set_preview_window
+}
+
+Return<void> CameraDevice::enableMsgType(uint32_t msgType) {
+ ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ if (!mDevice) {
+ ALOGE("%s called while camera is not opened", __FUNCTION__);
+ return Void();
+ }
+ if (mDevice->ops->enable_msg_type) {
+ mDevice->ops->enable_msg_type(mDevice, msgType);
+ }
+ return Void();
+}
+
+Return<void> CameraDevice::disableMsgType(uint32_t msgType) {
+ ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ if (!mDevice) {
+ ALOGE("%s called while camera is not opened", __FUNCTION__);
+ return Void();
+ }
+ if (mDevice->ops->disable_msg_type) {
+ mDevice->ops->disable_msg_type(mDevice, msgType);
+ }
+ return Void();
+}
+
+Return<bool> CameraDevice::msgTypeEnabled(uint32_t msgType) {
+ ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ if (!mDevice) {
+ ALOGE("%s called while camera is not opened", __FUNCTION__);
+ return false;
+ }
+ if (mDevice->ops->msg_type_enabled) {
+ return mDevice->ops->msg_type_enabled(mDevice, msgType);
+ }
+ return false;
+}
+
+Return<Status> CameraDevice::startPreview() {
+ ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ if (!mDevice) {
+ ALOGE("%s called while camera is not opened", __FUNCTION__);
+ return Status::OPERATION_NOT_SUPPORTED;
+ }
+ if (mDevice->ops->start_preview) {
+ return getHidlStatus(mDevice->ops->start_preview(mDevice));
+ }
+ return Status::INTERNAL_ERROR; // HAL should provide start_preview
+}
+
+Return<void> CameraDevice::stopPreview() {
+ ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ if (!mDevice) {
+ ALOGE("%s called while camera is not opened", __FUNCTION__);
+ return Void();
+ }
+ if (mDevice->ops->stop_preview) {
+ mDevice->ops->stop_preview(mDevice);
+ }
+ return Void();
+}
+
+Return<bool> CameraDevice::previewEnabled() {
+ ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ if (!mDevice) {
+ ALOGE("%s called while camera is not opened", __FUNCTION__);
+ return false;
+ }
+ if (mDevice->ops->preview_enabled) {
+ return mDevice->ops->preview_enabled(mDevice);
+ }
+ return false;
+}
+
+Return<Status> CameraDevice::storeMetaDataInBuffers(bool enable) {
+ ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ if (!mDevice) {
+ ALOGE("%s called while camera is not opened", __FUNCTION__);
+ return Status::OPERATION_NOT_SUPPORTED;
+ }
+ if (mDevice->ops->store_meta_data_in_buffers) {
+ status_t s = mDevice->ops->store_meta_data_in_buffers(mDevice, enable);
+ if (s == OK && enable) {
+ mMetadataMode = true;
+ }
+ return getHidlStatus(s);
+ }
+ return enable ? Status::ILLEGAL_ARGUMENT : Status::OK;
+}
+
+Return<Status> CameraDevice::startRecording() {
+ ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ if (!mDevice) {
+ ALOGE("%s called while camera is not opened", __FUNCTION__);
+ return Status::OPERATION_NOT_SUPPORTED;
+ }
+ if (mDevice->ops->start_recording) {
+ return getHidlStatus(mDevice->ops->start_recording(mDevice));
+ }
+ return Status::ILLEGAL_ARGUMENT;
+}
+
+Return<void> CameraDevice::stopRecording() {
+ ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ if (!mDevice) {
+ ALOGE("%s called while camera is not opened", __FUNCTION__);
+ return Void();
+ }
+ if (mDevice->ops->stop_recording) {
+ mDevice->ops->stop_recording(mDevice);
+ }
+ return Void();
+}
+
+Return<bool> CameraDevice::recordingEnabled() {
+ ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ if (!mDevice) {
+ ALOGE("%s called while camera is not opened", __FUNCTION__);
+ return false;
+ }
+ if (mDevice->ops->recording_enabled) {
+ return mDevice->ops->recording_enabled(mDevice);
+ }
+ return false;
+}
+
+void CameraDevice::releaseRecordingFrameLocked(
+ uint32_t memId, uint32_t bufferIndex, const native_handle_t* handle) {
+ if (!mDevice) {
+ ALOGE("%s called while camera is not opened", __FUNCTION__);
+ return;
+ }
+ if (mDevice->ops->release_recording_frame) {
+ CameraHeapMemory* camMemory = mMemoryMap.at(memId);
+ sp<MemoryHeapBase> heap = camMemory->mHeap;
+ if (bufferIndex >= camMemory->mNumBufs) {
+ ALOGE("%s: bufferIndex %d exceeds number of buffers %d",
+ __FUNCTION__, bufferIndex, camMemory->mNumBufs);
+ return;
+ }
+ sp<IMemory> mem = camMemory->mBuffers[bufferIndex];
+ // TODO: simplify below logic once we verify offset is indeed idx * mBufSize
+ // and heap == heap2
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap2 = mem->getMemory(&offset, &size);
+ if ((size_t)offset != bufferIndex * camMemory->mBufSize) {
+ ALOGI("%s: unexpected offset %zd (was expecting %zu)",
+ __FUNCTION__, offset, bufferIndex * camMemory->mBufSize);
+ }
+ if (heap != heap2) {
+ ALOGE("%s: heap mismatch!", __FUNCTION__);
+ return;
+ }
+ void *data = ((uint8_t *)heap->base()) + offset;
+ if (handle) {
+ VideoNativeHandleMetadata* md = (VideoNativeHandleMetadata*) data;
+ if (md->eType == VideoNativeHandleMetadata::kMetadataBufferTypeNativeHandleSource) {
+ // Input handle will be closed by HIDL transport later, so clone it
+ // HAL implementation is responsible to close/delete the clone
+ native_handle_t* clone = native_handle_clone(handle);
+ if (!clone) {
+ ALOGE("%s: failed to clone buffer %p", __FUNCTION__, handle);
+ return;
+ }
+ md->pHandle = clone;
+ } else {
+ ALOGE("%s:Malform VideoNativeHandleMetadata at memId %d, bufferId %d",
+ __FUNCTION__, memId, bufferIndex);
+ return;
+ }
+ }
+ mDevice->ops->release_recording_frame(mDevice, data);
+ }
+}
+
+Return<void> CameraDevice::releaseRecordingFrame(uint32_t memId, uint32_t bufferIndex) {
+ ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ releaseRecordingFrameLocked(memId, bufferIndex, nullptr);
+ return Void();
+}
+
+Return<void> CameraDevice::releaseRecordingFrameHandle(
+ uint32_t memId, uint32_t bufferIndex, const hidl_handle& frame) {
+ ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ releaseRecordingFrameLocked(
+ memId, bufferIndex, frame.getNativeHandle());
+ return Void();
+}
+
+Return<Status> CameraDevice::autoFocus() {
+ ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ if (!mDevice) {
+ ALOGE("%s called while camera is not opened", __FUNCTION__);
+ return Status::OPERATION_NOT_SUPPORTED;
+ }
+ if (mDevice->ops->auto_focus) {
+ return getHidlStatus(mDevice->ops->auto_focus(mDevice));
+ }
+ return Status::ILLEGAL_ARGUMENT;
+}
+
+Return<Status> CameraDevice::cancelAutoFocus() {
+ ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ if (!mDevice) {
+ ALOGE("%s called while camera is not opened", __FUNCTION__);
+ return Status::OPERATION_NOT_SUPPORTED;
+ }
+ if (mDevice->ops->cancel_auto_focus) {
+ return getHidlStatus(mDevice->ops->cancel_auto_focus(mDevice));
+ }
+ return Status::ILLEGAL_ARGUMENT;
+}
+
+Return<Status> CameraDevice::takePicture() {
+ ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ if (!mDevice) {
+ ALOGE("%s called while camera is not opened", __FUNCTION__);
+ return Status::OPERATION_NOT_SUPPORTED;
+ }
+ if (mDevice->ops->take_picture) {
+ return getHidlStatus(mDevice->ops->take_picture(mDevice));
+ }
+ return Status::ILLEGAL_ARGUMENT;
+}
+
+Return<Status> CameraDevice::cancelPicture() {
+ ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ if (!mDevice) {
+ ALOGE("%s called while camera is not opened", __FUNCTION__);
+ return Status::OPERATION_NOT_SUPPORTED;
+ }
+ if (mDevice->ops->cancel_picture) {
+ return getHidlStatus(mDevice->ops->cancel_picture(mDevice));
+ }
+ return Status::ILLEGAL_ARGUMENT;
+}
+
+Return<Status> CameraDevice::setParameters(const hidl_string& params) {
+ ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ if (!mDevice) {
+ ALOGE("%s called while camera is not opened", __FUNCTION__);
+ return Status::OPERATION_NOT_SUPPORTED;
+ }
+ if (mDevice->ops->set_parameters) {
+ return getHidlStatus(mDevice->ops->set_parameters(mDevice, params.c_str()));
+ }
+ return Status::ILLEGAL_ARGUMENT;
+}
+
+Return<void> CameraDevice::getParameters(getParameters_cb _hidl_cb) {
+ ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ hidl_string outStr;
+ if (!mDevice) {
+ ALOGE("%s called while camera is not opened", __FUNCTION__);
+ _hidl_cb(outStr);
+ return Void();
+ }
+ if (mDevice->ops->get_parameters) {
+ char *temp = mDevice->ops->get_parameters(mDevice);
+ outStr = temp;
+ if (mDevice->ops->put_parameters) {
+ mDevice->ops->put_parameters(mDevice, temp);
+ } else {
+ free(temp);
+ }
+ }
+ _hidl_cb(outStr);
+ return Void();
+}
+
+Return<Status> CameraDevice::sendCommand(CommandType cmd, int32_t arg1, int32_t arg2) {
+ ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ if (!mDevice) {
+ ALOGE("%s called while camera is not opened", __FUNCTION__);
+ return Status::OPERATION_NOT_SUPPORTED;
+ }
+ if (mDevice->ops->send_command) {
+ return getHidlStatus(mDevice->ops->send_command(mDevice, (int32_t) cmd, arg1, arg2));
+ }
+ return Status::ILLEGAL_ARGUMENT;
+}
+
+Return<void> CameraDevice::close() {
+ ALOGI("Closing camera %s", mCameraId.c_str());
+ Mutex::Autolock _l(mLock);
+ if(mDevice) {
+ int rc = mDevice->common.close(&mDevice->common);
+ if (rc != OK) {
+ ALOGE("Could not close camera %s: %d", mCameraId.c_str(), rc);
+ }
+ mDevice = nullptr;
+ }
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace device
+} // namespace camera
+} // namespace hardware
+} // namespace android
diff --git a/camera/device/1.0/default/CameraDevice_1_0.h b/camera/device/1.0/default/CameraDevice_1_0.h
new file mode 100644
index 0000000..2568f86
--- /dev/null
+++ b/camera/device/1.0/default/CameraDevice_1_0.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V1_0_CAMERADEVICE_H
+#define ANDROID_HARDWARE_CAMERA_DEVICE_V1_0_CAMERADEVICE_H
+
+#include <unordered_map>
+#include "utils/Mutex.h"
+#include "utils/SortedVector.h"
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
+#include "CameraModule.h"
+#include "HandleImporter.h"
+
+#include <android/hardware/camera/device/1.0/ICameraDevice.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::camera::common::V1_0::CameraResourceCost;
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::common::V1_0::TorchMode;
+using ::android::hardware::camera::common::V1_0::helper::CameraModule;
+using ::android::hardware::camera::common::V1_0::helper::HandleImporter;
+using ::android::hardware::camera::device::V1_0::CameraInfo;
+using ::android::hardware::camera::device::V1_0::CommandType;
+using ::android::hardware::camera::device::V1_0::ICameraDevice;
+using ::android::hardware::camera::device::V1_0::ICameraDeviceCallback;
+using ::android::hardware::camera::device::V1_0::ICameraDevicePreviewCallback;
+using ::android::hardware::camera::device::V1_0::MemoryId;
+using ::android::hidl::base::V1_0::IBase;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct CameraDevice : public ICameraDevice {
+
+ // Called by provider HAL. Provider HAL must ensure the uniqueness of
+ // CameraDevice object per cameraId, or there could be multiple CameraDevice
+ // trying to access the same physical camera.
+ // Also, provider will have to keep track of all CameraDevice objects in
+ // order to notify CameraDevice when the underlying camera is detached
+ CameraDevice(sp<CameraModule> module,
+ const std::string& cameraId,
+ const SortedVector<std::pair<std::string, std::string>>& cameraDeviceNames);
+ ~CameraDevice();
+
+ // Caller must use this method to check if CameraDevice ctor failed
+ bool isInitFailed() { return mInitFail; }
+ // Used by provider HAL to signal external camera disconnected
+ void setConnectionStatus(bool connected);
+
+ // Methods from ::android::hardware::camera::device::V1_0::ICameraDevice follow.
+ Return<void> getResourceCost(getResourceCost_cb _hidl_cb) override;
+ Return<void> getCameraInfo(getCameraInfo_cb _hidl_cb) override;
+ Return<Status> setTorchMode(TorchMode mode) override;
+ Return<Status> dumpState(const hidl_handle& fd) override;
+ Return<Status> open(const sp<ICameraDeviceCallback>& callback) override;
+ Return<Status> setPreviewWindow(const sp<ICameraDevicePreviewCallback>& window) override;
+ Return<void> enableMsgType(uint32_t msgType) override;
+ Return<void> disableMsgType(uint32_t msgType) override;
+ Return<bool> msgTypeEnabled(uint32_t msgType) override;
+ Return<Status> startPreview() override;
+ Return<void> stopPreview() override;
+ Return<bool> previewEnabled() override;
+ Return<Status> storeMetaDataInBuffers(bool enable) override;
+ Return<Status> startRecording() override;
+ Return<void> stopRecording() override;
+ Return<bool> recordingEnabled() override;
+ Return<void> releaseRecordingFrame(uint32_t memId, uint32_t bufferIndex) override;
+ Return<void> releaseRecordingFrameHandle(
+ uint32_t memId, uint32_t bufferIndex, const hidl_handle& frame) override;
+ Return<Status> autoFocus() override;
+ Return<Status> cancelAutoFocus() override;
+ Return<Status> takePicture() override;
+ Return<Status> cancelPicture() override;
+ Return<Status> setParameters(const hidl_string& params) override;
+ Return<void> getParameters(getParameters_cb _hidl_cb) override;
+ Return<Status> sendCommand(CommandType cmd, int32_t arg1, int32_t arg2) override;
+ Return<void> close() override;
+
+private:
+ struct CameraMemory : public camera_memory_t {
+ MemoryId mId;
+ CameraDevice* mDevice;
+ };
+
+ class CameraHeapMemory : public RefBase {
+ public:
+ CameraHeapMemory(int fd, size_t buf_size, uint_t num_buffers = 1);
+ explicit CameraHeapMemory(size_t buf_size, uint_t num_buffers = 1);
+ void commonInitialization();
+ virtual ~CameraHeapMemory();
+
+ size_t mBufSize;
+ uint_t mNumBufs;
+ // TODO: b/35887419: use hidl_memory instead and get rid of libbinder
+ sp<MemoryHeapBase> mHeap;
+ sp<MemoryBase>* mBuffers;
+ CameraMemory handle;
+ };
+
+ // TODO: b/35625849
+ // Meta data buffer layout for passing a native_handle to codec
+ // matching frameworks/native/include/media/hardware/MetadataBufferType.h and
+ // frameworks/native/include/media/hardware/HardwareAPI.h
+ struct VideoNativeHandleMetadata {
+ static const uint32_t kMetadataBufferTypeNativeHandleSource = 3;
+ uint32_t eType; // must be kMetadataBufferTypeNativeHandleSource
+ native_handle_t* pHandle;
+ };
+
+ const sp<CameraModule> mModule;
+ const std::string mCameraId;
+ // const after ctor
+ int mCameraIdInt;
+ int mDeviceVersion;
+
+ camera_device_t* mDevice = nullptr;
+
+ void initHalPreviewWindow();
+ struct CameraPreviewWindow : public preview_stream_ops {
+ // Called when we expect buffer will be re-allocated
+ void cleanUpCirculatingBuffers();
+
+ Mutex mLock;
+ sp<ICameraDevicePreviewCallback> mPreviewCallback = nullptr;
+ std::unordered_map<uint64_t, buffer_handle_t> mCirculatingBuffers;
+ std::unordered_map<buffer_handle_t*, uint64_t> mBufferIdMap;
+ } mHalPreviewWindow;
+
+ // gating access to mDevice, mInitFail, mDisconnected
+ mutable Mutex mLock;
+
+ bool mInitFail = false;
+ // Set by provider (when external camera is connected/disconnected)
+ bool mDisconnected;
+
+ static HandleImporter& sHandleImporter;
+
+ const SortedVector<std::pair<std::string, std::string>>& mCameraDeviceNames;
+
+ sp<ICameraDeviceCallback> mDeviceCallback = nullptr;
+
+ std::unordered_map<MemoryId, CameraHeapMemory*> mMemoryMap;
+
+ bool mMetadataMode = false;
+
+ void releaseRecordingFrameLocked(uint32_t memId, uint32_t bufferIndex, const native_handle_t*);
+
+ // shared memory methods
+ static camera_memory_t* sGetMemory(int fd, size_t buf_size, uint_t num_bufs, void *user);
+ static void sPutMemory(camera_memory_t *data);
+
+ // Device callback forwarding methods
+ static void sNotifyCb(int32_t msg_type, int32_t ext1, int32_t ext2, void *user);
+ static void sDataCb(int32_t msg_type, const camera_memory_t *data, unsigned int index,
+ camera_frame_metadata_t *metadata, void *user);
+ static void sDataCbTimestamp(nsecs_t timestamp, int32_t msg_type,
+ const camera_memory_t *data, unsigned index, void *user);
+
+ // Preview window callback forwarding methods
+ static int sDequeueBuffer(struct preview_stream_ops* w,
+ buffer_handle_t** buffer, int *stride);
+
+ static int sLockBuffer(struct preview_stream_ops* w, buffer_handle_t* buffer);
+
+ static int sEnqueueBuffer(struct preview_stream_ops* w, buffer_handle_t* buffer);
+
+ static int sCancelBuffer(struct preview_stream_ops* w, buffer_handle_t* buffer);
+
+ static int sSetBufferCount(struct preview_stream_ops* w, int count);
+
+ static int sSetBuffersGeometry(struct preview_stream_ops* w,
+ int width, int height, int format);
+
+ static int sSetCrop(struct preview_stream_ops *w, int left, int top, int right, int bottom);
+
+ static int sSetTimestamp(struct preview_stream_ops *w, int64_t timestamp);
+
+ static int sSetUsage(struct preview_stream_ops* w, int usage);
+
+ static int sSetSwapInterval(struct preview_stream_ops *w, int interval);
+
+ static int sGetMinUndequeuedBufferCount(const struct preview_stream_ops *w, int *count);
+
+ // convert conventional HAL status to HIDL Status
+ static Status getHidlStatus(const int&);
+ static status_t getStatusT(const Status& s);
+
+ Status initStatus() const;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace device
+} // namespace camera
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAMERA_DEVICE_V1_0_CAMERADEVICE_H
diff --git a/camera/device/1.0/types.hal b/camera/device/1.0/types.hal
index 83c0be4..b32c938 100644
--- a/camera/device/1.0/types.hal
+++ b/camera/device/1.0/types.hal
@@ -189,6 +189,66 @@
PREVIEW_METADATA = 0x0400
};
+/**
+ * Information for a single detected face.
+ */
+ struct CameraFace {
+ /**
+ * Bounds of the face [left, top, right, bottom]. (-1000, -1000) represents
+ * the top-left of the camera field of view, and (1000, 1000) represents the
+ * bottom-right of the field of view. The width and height cannot be 0 or
+ * negative. This is supported by both hardware and software face detection.
+ *
+ * The direction is relative to the sensor orientation, that is, what the
+ * sensor sees. The direction is not affected by the rotation or mirroring
+ * of CAMERA_CMD_SET_DISPLAY_ORIENTATION.
+ */
+ int32_t[4] rect;
+
+ /**
+ * The confidence level of the face. The range is 1 to 100. 100 is the
+ * highest confidence. This is supported by both hardware and software
+ * face detection.
+ */
+ int32_t score;
+
+ /**
+ * An unique id per face while the face is visible to the tracker. If
+ * the face leaves the field-of-view and comes back, it will get a new
+ * id. If the value is 0, id is not supported.
+ */
+ int32_t id;
+
+ /**
+ * The coordinates of the center of the left eye. The range is -1000 to
+ * 1000. -2000, -2000 if this is not supported.
+ */
+ int32_t[2] leftEye;
+
+ /**
+ * The coordinates of the center of the right eye. The range is -1000 to
+ * 1000. -2000, -2000 if this is not supported.
+ */
+ int32_t[2] rightEye;
+
+ /**
+ * The coordinates of the center of the mouth. The range is -1000 to 1000.
+ * -2000, -2000 if this is not supported.
+ */
+ int32_t[2] mouth;
+
+};
+
+/**
+ * The metadata of the frame data, such as face detection result.
+ */
+struct CameraFrameMetadata {
+ /**
+ * A vector of the detected faces.
+ */
+ vec<CameraFace> faces;
+};
+
/*
* A simple integer handle to use to reference a particular memory buffer
* between the HAL and the framework.
diff --git a/camera/device/3.2/default/CameraDevice.cpp b/camera/device/3.2/default/CameraDevice.cpp
index 18e0e7b..0a457ad 100644
--- a/camera/device/3.2/default/CameraDevice.cpp
+++ b/camera/device/3.2/default/CameraDevice.cpp
@@ -15,11 +15,11 @@
*/
#define LOG_TAG "CamDev@3.2-impl"
-#include <android/log.h>
+#include <utils/Log.h>
#include <utils/Vector.h>
#include <utils/Trace.h>
-#include "CameraDevice.h"
+#include "CameraDevice_3_2.h"
#include <include/convert.h>
namespace android {
diff --git a/camera/device/3.2/default/CameraDeviceSession.cpp b/camera/device/3.2/default/CameraDeviceSession.cpp
index 0f3d97b..3c3c078 100644
--- a/camera/device/3.2/default/CameraDeviceSession.cpp
+++ b/camera/device/3.2/default/CameraDeviceSession.cpp
@@ -29,198 +29,13 @@
namespace V3_2 {
namespace implementation {
-namespace {
-
-// Copy pasted from Hwc.cpp. Use this until gralloc mapper HAL is working
-class HandleImporter {
-public:
- HandleImporter() : mInitialized(false) {}
-
- bool initialize()
- {
- // allow only one client
- if (mInitialized) {
- return false;
- }
-
- if (!openGralloc()) {
- return false;
- }
-
- mInitialized = true;
- return true;
- }
-
- void cleanup()
- {
- if (!mInitialized) {
- return;
- }
-
- closeGralloc();
- mInitialized = false;
- }
-
- // In IComposer, any buffer_handle_t is owned by the caller and we need to
- // make a clone for hwcomposer2. We also need to translate empty handle
- // to nullptr. This function does that, in-place.
- bool importBuffer(buffer_handle_t& handle)
- {
- if (!handle->numFds && !handle->numInts) {
- handle = nullptr;
- return true;
- }
-
- buffer_handle_t clone = cloneBuffer(handle);
- if (!clone) {
- return false;
- }
-
- handle = clone;
- return true;
- }
-
- void freeBuffer(buffer_handle_t handle)
- {
- if (!handle) {
- return;
- }
-
- releaseBuffer(handle);
- }
-
- bool importFence(const native_handle_t* handle, int& fd)
- {
- if (handle == nullptr || handle->numFds == 0) {
- fd = -1;
- } else if (handle->numFds == 1) {
- fd = dup(handle->data[0]);
- if (fd < 0) {
- ALOGE("failed to dup fence fd %d", handle->data[0]);
- return false;
- }
- } else {
- ALOGE("invalid fence handle with %d file descriptors",
- handle->numFds);
- return false;
- }
-
- return true;
- }
-
- void closeFence(int fd)
- {
- if (fd >= 0) {
- close(fd);
- }
- }
-
-private:
- bool mInitialized;
-
- bool openGralloc()
- {
- const hw_module_t* module;
- int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
- if (err) {
- ALOGE("failed to get gralloc module");
- return false;
- }
-
- uint8_t major = (module->module_api_version >> 8) & 0xff;
- if (major > 1) {
- ALOGE("unknown gralloc module major version %d", major);
- return false;
- }
-
- if (major == 1) {
- err = gralloc1_open(module, &mDevice);
- if (err) {
- ALOGE("failed to open gralloc1 device");
- return false;
- }
-
- mRetain = reinterpret_cast<GRALLOC1_PFN_RETAIN>(
- mDevice->getFunction(mDevice, GRALLOC1_FUNCTION_RETAIN));
- mRelease = reinterpret_cast<GRALLOC1_PFN_RELEASE>(
- mDevice->getFunction(mDevice, GRALLOC1_FUNCTION_RELEASE));
- if (!mRetain || !mRelease) {
- ALOGE("invalid gralloc1 device");
- gralloc1_close(mDevice);
- return false;
- }
- } else {
- mModule = reinterpret_cast<const gralloc_module_t*>(module);
- }
-
- return true;
- }
-
- void closeGralloc()
- {
- if (mDevice) {
- gralloc1_close(mDevice);
- }
- }
-
- buffer_handle_t cloneBuffer(buffer_handle_t handle)
- {
- native_handle_t* clone = native_handle_clone(handle);
- if (!clone) {
- ALOGE("failed to clone buffer %p", handle);
- return nullptr;
- }
-
- bool err;
- if (mDevice) {
- err = (mRetain(mDevice, clone) != GRALLOC1_ERROR_NONE);
- } else {
- err = (mModule->registerBuffer(mModule, clone) != 0);
- }
-
- if (err) {
- ALOGE("failed to retain/register buffer %p", clone);
- native_handle_close(clone);
- native_handle_delete(clone);
- return nullptr;
- }
-
- return clone;
- }
-
- void releaseBuffer(buffer_handle_t handle)
- {
- if (mDevice) {
- mRelease(mDevice, handle);
- } else {
- mModule->unregisterBuffer(mModule, handle);
- }
- native_handle_close(handle);
- native_handle_delete(const_cast<native_handle_t*>(handle));
- }
-
- // gralloc1
- gralloc1_device_t* mDevice;
- GRALLOC1_PFN_RETAIN mRetain;
- GRALLOC1_PFN_RELEASE mRelease;
-
- // gralloc0
- const gralloc_module_t* mModule;
-};
-
-HandleImporter sHandleImporter;
-
-} // Anonymous namespace
+HandleImporter& CameraDeviceSession::sHandleImporter = HandleImporter::getInstance();
CameraDeviceSession::CameraDeviceSession(
camera3_device_t* device, const sp<ICameraDeviceCallback>& callback) :
camera3_callback_ops({&sProcessCaptureResult, &sNotify}),
mDevice(device),
mCallback(callback) {
- // For now, we init sHandleImporter but do not cleanup (keep it alive until
- // HAL process ends)
- sHandleImporter.initialize();
-
mInitFail = initialize();
}
diff --git a/camera/device/3.2/default/CameraDeviceSession.h b/camera/device/3.2/default/CameraDeviceSession.h
index f8689d3..3786e4b 100644
--- a/camera/device/3.2/default/CameraDeviceSession.h
+++ b/camera/device/3.2/default/CameraDeviceSession.h
@@ -26,6 +26,7 @@
#include <hidl/Status.h>
#include <hidl/MQDescriptor.h>
#include <include/convert.h>
+#include "HandleImporter.h"
namespace android {
namespace hardware {
@@ -39,6 +40,7 @@
using ::android::hardware::camera::device::V3_2::StreamConfiguration;
using ::android::hardware::camera::device::V3_2::ICameraDeviceSession;
using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::common::V1_0::helper::HandleImporter;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::hardware::hidl_vec;
@@ -109,6 +111,8 @@
// Stream ID -> circulating buffers map
std::map<int, CirculatingBuffers> mCirculatingBuffers;
+ static HandleImporter& sHandleImporter;
+
bool mInitFail;
bool initialize();
diff --git a/camera/device/3.2/default/CameraDevice.h b/camera/device/3.2/default/CameraDevice_3_2.h
similarity index 97%
rename from camera/device/3.2/default/CameraDevice.h
rename to camera/device/3.2/default/CameraDevice_3_2.h
index 317eea5..4e86067 100644
--- a/camera/device/3.2/default/CameraDevice.h
+++ b/camera/device/3.2/default/CameraDevice_3_2.h
@@ -55,7 +55,7 @@
// Called by provider HAL. Provider HAL must ensure the uniqueness of
// CameraDevice object per cameraId, or there could be multiple CameraDevice
// trying to access the same physical camera.
- // Also, provider will have to keep track of all CameraDevice object in
+ // Also, provider will have to keep track of all CameraDevice objects in
// order to notify CameraDevice when the underlying camera is detached
CameraDevice(sp<CameraModule> module,
const std::string& cameraId,
@@ -81,8 +81,7 @@
/* End of Methods from ::android::hardware::camera::device::V3_2::ICameraDevice */
private:
- // Passed from provider HAL. Should not change.
- sp<CameraModule> mModule;
+ const sp<CameraModule> mModule;
const std::string mCameraId;
// const after ctor
int mCameraIdInt;
diff --git a/camera/provider/2.4/default/Android.bp b/camera/provider/2.4/default/Android.bp
index 7315292..a6febec 100644
--- a/camera/provider/2.4/default/Android.bp
+++ b/camera/provider/2.4/default/Android.bp
@@ -10,6 +10,7 @@
"libcutils",
"android.hardware.camera.device@1.0",
"android.hardware.camera.device@3.2",
+ "camera.device@1.0-impl",
"camera.device@3.2-impl",
"android.hardware.camera.provider@2.4",
"android.hardware.camera.common@1.0",
@@ -34,15 +35,9 @@
"libhidltransport",
"liblog",
"libutils",
- "libhardware",
"android.hardware.camera.device@1.0",
"android.hardware.camera.device@3.2",
"android.hardware.camera.provider@2.4",
"android.hardware.camera.common@1.0",
- "libcutils",
- "libcamera_metadata"
],
- static_libs: [
- "android.hardware.camera.common@1.0-helper"
- ]
}
diff --git a/camera/provider/2.4/default/CameraProvider.cpp b/camera/provider/2.4/default/CameraProvider.cpp
index 5714f83..ad9f0b8 100644
--- a/camera/provider/2.4/default/CameraProvider.cpp
+++ b/camera/provider/2.4/default/CameraProvider.cpp
@@ -18,7 +18,8 @@
#include <android/log.h>
#include "CameraProvider.h"
-#include "CameraDevice.h"
+#include "CameraDevice_1_0.h"
+#include "CameraDevice_3_2.h"
#include <string.h>
#include <utils/Trace.h>
@@ -59,8 +60,6 @@
return;
}
- ALOGI("%s resolved provider %p", __FUNCTION__, cp);
-
Mutex::Autolock _l(cp->mCbLock);
char cameraId[kMaxCameraIdLen];
snprintf(cameraId, sizeof(cameraId), "%d", camera_id);
@@ -89,8 +88,6 @@
return;
}
- ALOGI("%s resolved provider %p", __FUNCTION__, cp);
-
Mutex::Autolock _l(cp->mCbLock);
if (cp->mCallbacks != nullptr) {
std::string cameraIdStr(camera_id);
@@ -323,9 +320,57 @@
}
Return<void> CameraProvider::getCameraDeviceInterface_V1_x(
- const hidl_string& /*cameraDeviceName*/, getCameraDeviceInterface_V1_x_cb _hidl_cb) {
- // TODO implement after device 1.0 is implemented
- _hidl_cb(Status::INTERNAL_ERROR, nullptr);
+ const hidl_string& cameraDeviceName, getCameraDeviceInterface_V1_x_cb _hidl_cb) {
+ std::smatch sm;
+ bool match = matchDeviceName(cameraDeviceName, sm);
+ if (!match) {
+ _hidl_cb(Status::ILLEGAL_ARGUMENT, nullptr);
+ return Void();
+ }
+
+ std::string cameraId = sm[2];
+ std::string deviceVersion = sm[1];
+ std::string deviceName(cameraDeviceName.c_str());
+ ssize_t index = mCameraDeviceNames.indexOf(std::make_pair(cameraId, deviceName));
+ if (index == NAME_NOT_FOUND) { // Either an illegal name or a device version mismatch
+ Status status = Status::OK;
+ ssize_t idx = mCameraIds.indexOf(cameraId);
+ if (idx == NAME_NOT_FOUND) {
+ ALOGE("%s: cannot find camera %s!", __FUNCTION__, cameraId.c_str());
+ status = Status::ILLEGAL_ARGUMENT;
+ } else { // invalid version
+ ALOGE("%s: camera device %s does not support version %s!",
+ __FUNCTION__, cameraId.c_str(), deviceVersion.c_str());
+ status = Status::OPERATION_NOT_SUPPORTED;
+ }
+ _hidl_cb(status, nullptr);
+ return Void();
+ }
+
+ if (mCameraStatusMap.count(cameraId) == 0 ||
+ mCameraStatusMap[cameraId] != CAMERA_DEVICE_STATUS_PRESENT) {
+ _hidl_cb(Status::ILLEGAL_ARGUMENT, nullptr);
+ return Void();
+ }
+
+ sp<android::hardware::camera::device::V1_0::implementation::CameraDevice> device =
+ new android::hardware::camera::device::V1_0::implementation::CameraDevice(
+ mModule, cameraId, mCameraDeviceNames);
+
+ if (device == nullptr) {
+ ALOGE("%s: cannot allocate camera device for id %s", __FUNCTION__, cameraId.c_str());
+ _hidl_cb(Status::INTERNAL_ERROR, nullptr);
+ return Void();
+ }
+
+ if (device->isInitFailed()) {
+ ALOGE("%s: camera device %s init failed!", __FUNCTION__, cameraId.c_str());
+ device = nullptr;
+ _hidl_cb(Status::INTERNAL_ERROR, nullptr);
+ return Void();
+ }
+
+ _hidl_cb (Status::OK, device);
return Void();
}
diff --git a/contexthub/1.0/vts/functional/VtsHalContexthubV1_0TargetTest.cpp b/contexthub/1.0/vts/functional/VtsHalContexthubV1_0TargetTest.cpp
index c5eea89..40a05e6 100644
--- a/contexthub/1.0/vts/functional/VtsHalContexthubV1_0TargetTest.cpp
+++ b/contexthub/1.0/vts/functional/VtsHalContexthubV1_0TargetTest.cpp
@@ -42,8 +42,6 @@
using ::android::hardware::contexthub::V1_0::TransactionResult;
using ::android::sp;
-#define CONTEXTHUB_SERVICE_NAME "contexthub"
-
#define ASSERT_OK(result) ASSERT_EQ(result, Result::OK)
#define EXPECT_OK(result) EXPECT_EQ(result, Result::OK)
@@ -80,7 +78,7 @@
static std::vector<uint32_t> hubIds;
if (hubIds.size() == 0) {
- sp<IContexthub> hubApi = ::testing::VtsHalHidlTargetBaseTest::getService<IContexthub>(CONTEXTHUB_SERVICE_NAME);
+ sp<IContexthub> hubApi = ::testing::VtsHalHidlTargetBaseTest::getService<IContexthub>();
if (hubApi != nullptr) {
for (ContextHub hub : getHubsSync(hubApi)) {
@@ -98,7 +96,7 @@
class ContexthubHidlTestBase : public ::testing::VtsHalHidlTargetBaseTest {
public:
virtual void SetUp() override {
- hubApi = ::testing::VtsHalHidlTargetBaseTest::getService<IContexthub>(CONTEXTHUB_SERVICE_NAME);
+ hubApi = ::testing::VtsHalHidlTargetBaseTest::getService<IContexthub>();
ASSERT_NE(hubApi, nullptr);
// getHubs() must be called at least once for proper initialization of the
diff --git a/graphics/common/1.0/types.hal b/graphics/common/1.0/types.hal
index 7663701..1ddd892 100644
--- a/graphics/common/1.0/types.hal
+++ b/graphics/common/1.0/types.hal
@@ -792,7 +792,7 @@
TRANSFER_GAMMA2_8 = 6 << TRANSFER_SHIFT,
/*
- * SMPTE ST 2084
+ * SMPTE ST 2084 (Dolby Perceptual Quantizer)
*
* Transfer characteristic curve:
* E = ((c1 + c2 * L^n) / (1 + c3 * L^n)) ^ m
@@ -1068,6 +1068,15 @@
*/
BT2020 = STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_FULL,
+ /*
+ * ITU-R Recommendation 2020 (BT.2020)
+ *
+ * Ultra High-definition television
+ *
+ * Use full range, SMPTE 2084 (PQ) transfer and BT2020 standard
+ */
+ BT2020_PQ = STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL,
+
/*
* Data spaces for non-color formats
diff --git a/media/omx/1.0/IGraphicBufferSource.hal b/media/omx/1.0/IGraphicBufferSource.hal
index 4dbfd32..62073ad 100644
--- a/media/omx/1.0/IGraphicBufferSource.hal
+++ b/media/omx/1.0/IGraphicBufferSource.hal
@@ -29,25 +29,25 @@
*/
interface IGraphicBufferSource {
- configure(IOmxNode omxNode, Dataspace dataspace);
+ configure(IOmxNode omxNode, Dataspace dataspace) generates (Status status);
- setSuspend(bool suspend, int64_t timeUs);
+ setSuspend(bool suspend, int64_t timeUs) generates (Status status);
- setRepeatPreviousFrameDelayUs(int64_t repeatAfterUs);
+ setRepeatPreviousFrameDelayUs(int64_t repeatAfterUs) generates (Status status);
- setMaxFps(float maxFps);
+ setMaxFps(float maxFps) generates (Status status);
- setTimeLapseConfig(int64_t timePerFrameUs, int64_t timePerCaptureUs);
+ setTimeLapseConfig(int64_t timePerFrameUs, int64_t timePerCaptureUs) generates (Status status);
- setStartTimeUs(int64_t startTimeUs);
+ setStartTimeUs(int64_t startTimeUs) generates (Status status);
- setStopTimeUs(int64_t stopTimeUs);
+ setStopTimeUs(int64_t stopTimeUs) generates (Status status);
- setColorAspects(ColorAspects aspects);
+ setColorAspects(ColorAspects aspects) generates (Status status);
- setTimeOffsetUs(int64_t timeOffsetUs);
+ setTimeOffsetUs(int64_t timeOffsetUs) generates (Status status);
- signalEndOfInputStream();
+ signalEndOfInputStream() generates (Status status);
};
diff --git a/nfc/1.0/vts/functional/VtsHalNfcV1_0TargetTest.cpp b/nfc/1.0/vts/functional/VtsHalNfcV1_0TargetTest.cpp
index 5b6089d..4aa6d7e 100644
--- a/nfc/1.0/vts/functional/VtsHalNfcV1_0TargetTest.cpp
+++ b/nfc/1.0/vts/functional/VtsHalNfcV1_0TargetTest.cpp
@@ -325,18 +325,42 @@
}
/*
+ * PowerCycleAfterClose:
+ * Calls powerCycle() after close()
+ * Checks status
+ */
+TEST_F(NfcHidlTest, PowerCycleAfterClose) {
+ EXPECT_EQ(NfcStatus::OK, nfc_->close());
+ // Wait for CLOSE_CPLT event
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(NfcEvent::CLOSE_CPLT, last_event_);
+ EXPECT_EQ(NfcStatus::OK, last_status_);
+
+ EXPECT_EQ(NfcStatus::FAILED, nfc_->powerCycle());
+
+ EXPECT_EQ(NfcStatus::OK, nfc_->open(nfc_cb_));
+ // Wait for OPEN_CPLT event
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(NfcEvent::OPEN_CPLT, last_event_);
+ EXPECT_EQ(NfcStatus::OK, last_status_);
+}
+
+/*
* CoreInitialized:
- * Calls coreInitialized()
+ * Calls coreInitialized() with different data
* Waits for NfcEvent.POST_INIT_CPLT
*/
TEST_F(NfcHidlTest, CoreInitialized) {
NfcData data;
data.resize(1);
- data[0] = 0;
- EXPECT_EQ(NfcStatus::OK, nfc_->coreInitialized(data));
- // Wait for NfcEvent.POST_INIT_CPLT
- EXPECT_EQ(std::cv_status::no_timeout, wait());
- EXPECT_EQ(NfcEvent::POST_INIT_CPLT, last_event_);
+ for (int i = 0; i <= 6; i++)
+ {
+ data[0] = i;
+ EXPECT_EQ(NfcStatus::OK, nfc_->coreInitialized(data));
+ // Wait for NfcEvent.POST_INIT_CPLT
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(NfcEvent::POST_INIT_CPLT, last_event_);
+ }
}
/*
@@ -348,6 +372,27 @@
EXPECT_EQ(NfcStatus::OK, nfc_->controlGranted());
}
+/*
+ * ControlGrantedAfterClose:
+ * Call controlGranted() after close
+ * Checks the return value
+ */
+TEST_F(NfcHidlTest, ControlGrantedAfterClose) {
+ EXPECT_EQ(NfcStatus::OK, nfc_->close());
+ // Wait for CLOSE_CPLT event
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(NfcEvent::CLOSE_CPLT, last_event_);
+ EXPECT_EQ(NfcStatus::OK, last_status_);
+
+ EXPECT_EQ(NfcStatus::OK, nfc_->controlGranted());
+
+ EXPECT_EQ(NfcStatus::OK, nfc_->open(nfc_cb_));
+ // Wait for OPEN_CPLT event
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(NfcEvent::OPEN_CPLT, last_event_);
+ EXPECT_EQ(NfcStatus::OK, last_status_);
+}
+
/* PreDiscover:
* Calls prediscover()
* Checks the return value
@@ -356,6 +401,59 @@
EXPECT_EQ(NfcStatus::OK, nfc_->prediscover());
}
+/*
+ * PreDiscoverAfterClose:
+ * Call prediscover() after close
+ * Checks the return value
+ */
+TEST_F(NfcHidlTest, PreDiscoverAfterClose) {
+ EXPECT_EQ(NfcStatus::OK, nfc_->close());
+ // Wait for CLOSE_CPLT event
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(NfcEvent::CLOSE_CPLT, last_event_);
+ EXPECT_EQ(NfcStatus::OK, last_status_);
+
+ EXPECT_EQ(NfcStatus::OK, nfc_->prediscover());
+
+ EXPECT_EQ(NfcStatus::OK, nfc_->open(nfc_cb_));
+ // Wait for OPEN_CPLT event
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(NfcEvent::OPEN_CPLT, last_event_);
+ EXPECT_EQ(NfcStatus::OK, last_status_);
+}
+
+/*
+ * CloseAfterClose:
+ * Calls close() multiple times
+ * Checks status
+ */
+TEST_F(NfcHidlTest, CloseAfterClose) {
+ EXPECT_EQ(NfcStatus::OK, nfc_->close());
+ // Wait for CLOSE_CPLT event
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(NfcEvent::CLOSE_CPLT, last_event_);
+ EXPECT_EQ(NfcStatus::OK, last_status_);
+
+ EXPECT_EQ(NfcStatus::FAILED, nfc_->close());
+
+ EXPECT_EQ(NfcStatus::OK, nfc_->open(nfc_cb_));
+ // Wait for OPEN_CPLT event
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(NfcEvent::OPEN_CPLT, last_event_);
+ EXPECT_EQ(NfcStatus::OK, last_status_);
+}
+
+
+/*
+ * OpenAfterOpen:
+ * Calls open() multiple times
+ * Checks status
+ */
+TEST_F(NfcHidlTest, OpenAfterOpen) {
+ EXPECT_EQ(NfcStatus::OK, nfc_->open(nfc_cb_));
+ EXPECT_EQ(NfcStatus::OK, nfc_->open(nfc_cb_));
+}
+
int main(int argc, char** argv) {
::testing::AddGlobalTestEnvironment(new NfcHidlEnvironment);
::testing::InitGoogleTest(&argc, argv);
diff --git a/radio/1.0/IRadio.hal b/radio/1.0/IRadio.hal
index a8c9d93..b3e3e98 100644
--- a/radio/1.0/IRadio.hal
+++ b/radio/1.0/IRadio.hal
@@ -731,20 +731,6 @@
oneway getDataCallList(int32_t serial);
/*
- * Indicates the current state of the screen. When the screen is off, the
- * Radio must notify the baseband to suppress certain notifications (eg,
- * signal strength and changes in LAC/CID or BID/SID/NID/latitude/longitude)
- * in an effort to conserve power. These notifications must resume when the
- * screen is on.
- *
- * @param serial Serial number of request.
- * @param enable true = screen on, false = screen off.
- *
- * Response function is IRadioResponse.sendScreenStateResponse()
- */
- oneway sendScreenState(int32_t serial, bool enable);
-
- /*
* Enables/disables supplementary service related notifications from the network.
* Notifications are reported via unsolSuppSvcNotification().
*
diff --git a/radio/1.0/IRadioResponse.hal b/radio/1.0/IRadioResponse.hal
index 11a1d03..cd0899a 100644
--- a/radio/1.0/IRadioResponse.hal
+++ b/radio/1.0/IRadioResponse.hal
@@ -928,17 +928,6 @@
* RadioError:NONE
* RadioError:RADIO_NOT_AVAILABLE
* RadioError:INVALID_ARGUMENTS
- * RadioError:GENERIC_FAILURE
- */
- oneway sendScreenStateResponse(RadioResponseInfo info);
-
- /*
- * @param info Response info struct containing response type, serial no. and error
- *
- * Valid errors returned:
- * RadioError:NONE
- * RadioError:RADIO_NOT_AVAILABLE
- * RadioError:INVALID_ARGUMENTS
* RadioError:SIM_BUSY
* RadioError:NO_MEMORY
* RadioError:SYSTEM_ERR
diff --git a/radio/1.0/vts/functional/radio_hidl_hal_utils.h b/radio/1.0/vts/functional/radio_hidl_hal_utils.h
index 0429226..1c58a97 100644
--- a/radio/1.0/vts/functional/radio_hidl_hal_utils.h
+++ b/radio/1.0/vts/functional/radio_hidl_hal_utils.h
@@ -234,8 +234,6 @@
Return<void> sendOemRilRequestStringsResponse(const RadioResponseInfo& info,
const ::android::hardware::hidl_vec<::android::hardware::hidl_string>& data);
- Return<void> sendScreenStateResponse(const RadioResponseInfo& info);
-
Return<void> setSuppServiceNotificationsResponse(
const RadioResponseInfo& info);
diff --git a/radio/1.0/vts/functional/radio_response.cpp b/radio/1.0/vts/functional/radio_response.cpp
index 2b4f10f..64b09c9 100644
--- a/radio/1.0/vts/functional/radio_response.cpp
+++ b/radio/1.0/vts/functional/radio_response.cpp
@@ -314,11 +314,6 @@
return Void();
}
-Return<void> RadioResponse::sendScreenStateResponse(
- const RadioResponseInfo& /*info*/) {
- return Void();
-}
-
Return<void> RadioResponse::setSuppServiceNotificationsResponse(
const RadioResponseInfo& /*info*/) {
return Void();
diff --git a/sensors/1.0/default/Sensors.cpp b/sensors/1.0/default/Sensors.cpp
index 41eb945..37e2b81 100644
--- a/sensors/1.0/default/Sensors.cpp
+++ b/sensors/1.0/default/Sensors.cpp
@@ -145,22 +145,41 @@
}
Return<void> Sensors::poll(int32_t maxCount, poll_cb _hidl_cb) {
+
hidl_vec<Event> out;
hidl_vec<SensorInfo> dynamicSensorsAdded;
- if (maxCount <= 0) {
- _hidl_cb(Result::BAD_VALUE, out, dynamicSensorsAdded);
- return Void();
+ std::unique_ptr<sensors_event_t[]> data;
+ int err = android::NO_ERROR;
+
+ { // scope of reentry lock
+
+ // This enforces a single client, meaning that a maximum of one client can call poll().
+ // If this function is re-entred, it means that we are stuck in a state that may prevent
+ // the system from proceeding normally.
+ //
+ // Exit and let the system restart the sensor-hal-implementation hidl service.
+ //
+ // This function must not call _hidl_cb(...) or return until there is no risk of blocking.
+ std::unique_lock<std::mutex> lock(mPollLock, std::try_to_lock);
+ if(!lock.owns_lock()){
+ // cannot get the lock, hidl service will go into deadlock if it is not restarted.
+ // This is guaranteed to not trigger in passthrough mode.
+ LOG(FATAL) <<
+ "ISensors::poll() re-entry. I do not know what to do except killing myself.";
+ }
+
+ if (maxCount <= 0) {
+ err = android::BAD_VALUE;
+ } else {
+ int bufferSize = maxCount <= kPollMaxBufferSize ? maxCount : kPollMaxBufferSize;
+ data.reset(new sensors_event_t[bufferSize]);
+ err = mSensorDevice->poll(
+ reinterpret_cast<sensors_poll_device_t *>(mSensorDevice),
+ data.get(), bufferSize);
+ }
}
- int bufferSize = maxCount <= kPollMaxBufferSize ? maxCount : kPollMaxBufferSize;
-
- std::unique_ptr<sensors_event_t[]> data(new sensors_event_t[bufferSize]);
-
- int err = mSensorDevice->poll(
- reinterpret_cast<sensors_poll_device_t *>(mSensorDevice),
- data.get(), bufferSize);
-
if (err < 0) {
_hidl_cb(ResultFromStatus(err), out, dynamicSensorsAdded);
return Void();
diff --git a/sensors/1.0/default/Sensors.h b/sensors/1.0/default/Sensors.h
index 09729d3..7d715e0 100644
--- a/sensors/1.0/default/Sensors.h
+++ b/sensors/1.0/default/Sensors.h
@@ -20,6 +20,7 @@
#include <android/hardware/sensors/1.0/ISensors.h>
#include <hardware/sensors.h>
+#include <mutex>
namespace android {
namespace hardware {
@@ -65,6 +66,7 @@
status_t mInitCheck;
sensors_module_t *mSensorModule;
sensors_poll_device_1_t *mSensorDevice;
+ std::mutex mPollLock;
int getHalDeviceVersion() const;
diff --git a/sensors/1.0/default/convert.cpp b/sensors/1.0/default/convert.cpp
index 748a963..306d3a3 100644
--- a/sensors/1.0/default/convert.cpp
+++ b/sensors/1.0/default/convert.cpp
@@ -25,8 +25,8 @@
namespace implementation {
void convertFromSensor(const sensor_t &src, SensorInfo *dst) {
- dst->name = src.name == nullptr ? "" : src.name;
- dst->vendor = src.vendor == nullptr ? "" : src.vendor;
+ dst->name = src.name;
+ dst->vendor = src.vendor;
dst->version = src.version;
dst->sensorHandle = src.handle;
dst->type = (SensorType)src.type;
@@ -36,8 +36,8 @@
dst->minDelay = src.minDelay;
dst->fifoReservedEventCount = src.fifoReservedEventCount;
dst->fifoMaxEventCount = src.fifoMaxEventCount;
- dst->typeAsString = src.stringType == nullptr ? "" : src.stringType;
- dst->requiredPermission = src.requiredPermission == nullptr ? "" : src.requiredPermission;
+ dst->typeAsString = src.stringType;
+ dst->requiredPermission = src.requiredPermission;
dst->maxDelay = src.maxDelay;
dst->flags = src.flags;
}
diff --git a/soundtrigger/2.0/default/SoundTriggerHalImpl.h b/soundtrigger/2.0/default/SoundTriggerHalImpl.h
index 4e0d01d..8aa9285 100644
--- a/soundtrigger/2.0/default/SoundTriggerHalImpl.h
+++ b/soundtrigger/2.0/default/SoundTriggerHalImpl.h
@@ -20,6 +20,7 @@
#include <android/hardware/soundtrigger/2.0/ISoundTriggerHw.h>
#include <android/hardware/soundtrigger/2.0/ISoundTriggerHwCallback.h>
#include <hidl/Status.h>
+#include <stdatomic.h>
#include <utils/threads.h>
#include <utils/KeyedVector.h>
#include <system/sound_trigger.h>
diff --git a/soundtrigger/2.0/vts/functional/VtsHalSoundtriggerV2_0TargetTest.cpp b/soundtrigger/2.0/vts/functional/VtsHalSoundtriggerV2_0TargetTest.cpp
index 41e7e69..273ee14 100644
--- a/soundtrigger/2.0/vts/functional/VtsHalSoundtriggerV2_0TargetTest.cpp
+++ b/soundtrigger/2.0/vts/functional/VtsHalSoundtriggerV2_0TargetTest.cpp
@@ -15,6 +15,12 @@
*/
#define LOG_TAG "SoundTriggerHidlHalTest"
+#include <stdlib.h>
+#include <time.h>
+
+#include <condition_variable>
+#include <mutex>
+
#include <android/log.h>
#include <cutils/native_handle.h>
@@ -24,6 +30,8 @@
#include <VtsHalHidlTargetBaseTest.h>
+#define SHORT_TIMEOUT_PERIOD (1)
+
using ::android::hardware::audio::common::V2_0::AudioDevice;
using ::android::hardware::soundtrigger::V2_0::SoundModelHandle;
using ::android::hardware::soundtrigger::V2_0::SoundModelType;
@@ -36,43 +44,101 @@
using ::android::hardware::Void;
using ::android::sp;
+/**
+ * Test code uses this class to wait for notification from callback.
+ */
+class Monitor {
+ public:
+ Monitor() : mCount(0) {}
+
+ /**
+ * Adds 1 to the internal counter and unblocks one of the waiting threads.
+ */
+ void notify() {
+ std::unique_lock<std::mutex> lock(mMtx);
+ mCount++;
+ mCv.notify_one();
+ }
+
+ /**
+ * Blocks until the internal counter becomes greater than 0.
+ *
+ * If notified, this method decreases the counter by 1 and returns true.
+ * If timeout, returns false.
+ */
+ bool wait(int timeoutSeconds) {
+ std::unique_lock<std::mutex> lock(mMtx);
+ auto deadline = std::chrono::system_clock::now() +
+ std::chrono::seconds(timeoutSeconds);
+ while (mCount == 0) {
+ if (mCv.wait_until(lock, deadline) == std::cv_status::timeout) {
+ return false;
+ }
+ }
+ mCount--;
+ return true;
+ }
+
+ private:
+ std::mutex mMtx;
+ std::condition_variable mCv;
+ int mCount;
+};
+
// The main test class for Sound Trigger HIDL HAL.
class SoundTriggerHidlTest : public ::testing::VtsHalHidlTargetBaseTest {
public:
virtual void SetUp() override {
mSoundTriggerHal = ::testing::VtsHalHidlTargetBaseTest::getService<ISoundTriggerHw>("sound_trigger.primary");
ASSERT_NE(nullptr, mSoundTriggerHal.get());
- mCallback = new MyCallback();
+ mCallback = new SoundTriggerHwCallback(*this);
ASSERT_NE(nullptr, mCallback.get());
}
- class MyCallback : public ISoundTriggerHwCallback {
- virtual Return<void> recognitionCallback(
- const ISoundTriggerHwCallback::RecognitionEvent& event __unused,
- int32_t cookie __unused) {
- ALOGI("%s", __FUNCTION__);
- return Void();
- }
+ static void SetUpTestCase() {
+ srand(time(nullptr));
+ }
- virtual Return<void> phraseRecognitionCallback(
- const ISoundTriggerHwCallback::PhraseRecognitionEvent& event __unused,
- int32_t cookie __unused) {
- ALOGI("%s", __FUNCTION__);
- return Void();
- }
+ class SoundTriggerHwCallback : public ISoundTriggerHwCallback {
+ private:
+ SoundTriggerHidlTest& mParent;
- virtual Return<void> soundModelCallback(
- const ISoundTriggerHwCallback::ModelEvent& event __unused,
- int32_t cookie __unused) {
- ALOGI("%s", __FUNCTION__);
- return Void();
- }
+ public:
+ SoundTriggerHwCallback(SoundTriggerHidlTest& parent) : mParent(parent) {}
+
+ virtual Return<void> recognitionCallback(
+ const ISoundTriggerHwCallback::RecognitionEvent& event __unused,
+ int32_t cookie __unused) {
+ ALOGI("%s", __FUNCTION__);
+ return Void();
+ }
+
+ virtual Return<void> phraseRecognitionCallback(
+ const ISoundTriggerHwCallback::PhraseRecognitionEvent& event __unused,
+ int32_t cookie __unused) {
+ ALOGI("%s", __FUNCTION__);
+ return Void();
+ }
+
+ virtual Return<void> soundModelCallback(
+ const ISoundTriggerHwCallback::ModelEvent& event,
+ int32_t cookie __unused) {
+ ALOGI("%s", __FUNCTION__);
+ mParent.lastModelEvent = event;
+ mParent.monitor.notify();
+ return Void();
+ }
};
virtual void TearDown() override {}
+ Monitor monitor;
+ // updated by soundModelCallback()
+ ISoundTriggerHwCallback::ModelEvent lastModelEvent;
+
+ protected:
sp<ISoundTriggerHw> mSoundTriggerHal;
- sp<MyCallback> mCallback;
+ sp<SoundTriggerHwCallback> mCallback;
};
// A class for test environment setup (kept since this file is a template).
@@ -137,6 +203,36 @@
EXPECT_TRUE(hidlReturn.isOk());
EXPECT_NE(0, ret);
+ EXPECT_FALSE(monitor.wait(SHORT_TIMEOUT_PERIOD));
+}
+
+/**
+ * Test ISoundTriggerHw::loadSoundModel() method
+ *
+ * Verifies that:
+ * - the implementation returns error when passed a sound model with random data.
+ */
+TEST_F(SoundTriggerHidlTest, LoadGenericSoundModelFail) {
+ int ret = -ENODEV;
+ ISoundTriggerHw::SoundModel model;
+ SoundModelHandle handle = 0;
+
+ model.type = SoundModelType::GENERIC;
+ model.data.resize(100);
+ for (auto& d : model.data) {
+ d = rand();
+ }
+
+ Return<void> loadReturn = mSoundTriggerHal->loadSoundModel(
+ model,
+ mCallback, 0, [&](int32_t retval, auto res) {
+ ret = retval;
+ handle = res;
+ });
+
+ EXPECT_TRUE(loadReturn.isOk());
+ EXPECT_NE(0, ret);
+ EXPECT_FALSE(monitor.wait(SHORT_TIMEOUT_PERIOD));
}
/**
diff --git a/tests/msgq/1.0/Android.bp b/tests/msgq/1.0/Android.bp
index d17efe4..2d42699 100644
--- a/tests/msgq/1.0/Android.bp
+++ b/tests/msgq/1.0/Android.bp
@@ -3,6 +3,7 @@
filegroup {
name: "android.hardware.tests.msgq@1.0_hal",
srcs: [
+ "IBenchmarkMsgQ.hal",
"ITestMsgQ.hal",
],
}
@@ -15,6 +16,7 @@
":android.hardware.tests.msgq@1.0_hal",
],
out: [
+ "android/hardware/tests/msgq/1.0/BenchmarkMsgQAll.cpp",
"android/hardware/tests/msgq/1.0/TestMsgQAll.cpp",
],
}
@@ -27,6 +29,11 @@
":android.hardware.tests.msgq@1.0_hal",
],
out: [
+ "android/hardware/tests/msgq/1.0/IBenchmarkMsgQ.h",
+ "android/hardware/tests/msgq/1.0/IHwBenchmarkMsgQ.h",
+ "android/hardware/tests/msgq/1.0/BnHwBenchmarkMsgQ.h",
+ "android/hardware/tests/msgq/1.0/BpHwBenchmarkMsgQ.h",
+ "android/hardware/tests/msgq/1.0/BsBenchmarkMsgQ.h",
"android/hardware/tests/msgq/1.0/ITestMsgQ.h",
"android/hardware/tests/msgq/1.0/IHwTestMsgQ.h",
"android/hardware/tests/msgq/1.0/BnHwTestMsgQ.h",
diff --git a/benchmarks/msgq/1.0/IBenchmarkMsgQ.hal b/tests/msgq/1.0/IBenchmarkMsgQ.hal
similarity index 98%
rename from benchmarks/msgq/1.0/IBenchmarkMsgQ.hal
rename to tests/msgq/1.0/IBenchmarkMsgQ.hal
index c4b9d95..81754a4 100644
--- a/benchmarks/msgq/1.0/IBenchmarkMsgQ.hal
+++ b/tests/msgq/1.0/IBenchmarkMsgQ.hal
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.hardware.benchmarks.msgq@1.0;
+package android.hardware.tests.msgq@1.0;
interface IBenchmarkMsgQ {
/*
diff --git a/wifi/1.0/default/android.hardware.wifi@1.0-service.rc b/wifi/1.0/default/android.hardware.wifi@1.0-service.rc
index c0ae4d4..696b1f9 100644
--- a/wifi/1.0/default/android.hardware.wifi@1.0-service.rc
+++ b/wifi/1.0/default/android.hardware.wifi@1.0-service.rc
@@ -1,4 +1,4 @@
service wifi_hal_legacy /vendor/bin/hw/android.hardware.wifi@1.0-service
class hal
user wifi
- group wifi
+ group wifi gps
diff --git a/wifi/1.0/default/hidl_struct_util.cpp b/wifi/1.0/default/hidl_struct_util.cpp
index 82364cb..485ae7e 100644
--- a/wifi/1.0/default/hidl_struct_util.cpp
+++ b/wifi/1.0/default/hidl_struct_util.cpp
@@ -96,7 +96,7 @@
if (!hidl_caps) {
return false;
}
- *hidl_caps = 0;
+ *hidl_caps = {};
using HidlChipCaps = IWifiChip::ChipCapabilityMask;
for (const auto feature : {legacy_hal::WIFI_LOGGER_MEMORY_DUMP_SUPPORTED,
legacy_hal::WIFI_LOGGER_DRIVER_DUMP_SUPPORTED,
@@ -133,6 +133,7 @@
if (!hidl_status) {
return false;
}
+ *hidl_status = {};
hidl_status->ringName = reinterpret_cast<const char*>(legacy_status.name);
hidl_status->flags = 0;
for (const auto flag : {WIFI_RING_BUFFER_FLAG_HAS_BINARY_ENTRIES,
@@ -165,7 +166,7 @@
if (!hidl_status_vec) {
return false;
}
- hidl_status_vec->clear();
+ *hidl_status_vec = {};
for (const auto& legacy_status : legacy_status_vec) {
WifiDebugRingBufferStatus hidl_status;
if (!convertLegacyDebugRingBufferStatusToHidl(legacy_status,
@@ -183,6 +184,7 @@
if (!hidl_stats) {
return false;
}
+ *hidl_stats = {};
hidl_stats->totalCmdEventWakeCnt =
legacy_stats.wake_reason_cnt.total_cmd_event_wake;
hidl_stats->cmdEventWakeCntPerType = legacy_stats.cmd_event_wake_cnt;
@@ -227,6 +229,7 @@
if (!hidl_caps) {
return false;
}
+ *hidl_caps = {};
*hidl_caps = 0;
using HidlStaIfaceCaps = IWifiStaIface::StaIfaceCapabilityMask;
for (const auto feature : {legacy_hal::WIFI_LOGGER_PACKET_FATE_SUPPORTED}) {
@@ -263,6 +266,7 @@
if (!hidl_caps) {
return false;
}
+ *hidl_caps = {};
hidl_caps->version = legacy_caps.version;
hidl_caps->maxLength = legacy_caps.max_len;
return true;
@@ -299,6 +303,7 @@
if (!hidl_caps) {
return false;
}
+ *hidl_caps = {};
hidl_caps->maxCacheSize = legacy_caps.max_scan_cache_size;
hidl_caps->maxBuckets = legacy_caps.max_scan_buckets;
hidl_caps->maxApCachePerScan = legacy_caps.max_ap_cache_per_scan;
@@ -332,6 +337,7 @@
if (!legacy_scan_params) {
return false;
}
+ *legacy_scan_params = {};
legacy_scan_params->base_period = hidl_scan_params.basePeriodInMs;
legacy_scan_params->max_ap_per_scan = hidl_scan_params.maxApPerScan;
legacy_scan_params->report_threshold_percent =
@@ -350,7 +356,7 @@
legacy_scan_params->buckets[bucket_idx];
legacy_bucket_spec.bucket = bucket_idx;
legacy_bucket_spec.band =
- static_cast<legacy_hal::wifi_band>(hidl_bucket_spec.band);
+ convertHidlWifiBandToLegacy(hidl_bucket_spec.band);
legacy_bucket_spec.period = hidl_bucket_spec.periodInMs;
legacy_bucket_spec.max_period = hidl_bucket_spec.exponentialMaxPeriodInMs;
legacy_bucket_spec.base = hidl_bucket_spec.exponentialBase;
@@ -384,6 +390,7 @@
if (!hidl_ie) {
return false;
}
+ *hidl_ie = {};
hidl_ie->id = legacy_ie.id;
hidl_ie->data =
std::vector<uint8_t>(legacy_ie.data, legacy_ie.data + legacy_ie.len);
@@ -396,6 +403,7 @@
if (!ie_blob || !hidl_ies) {
return false;
}
+ *hidl_ies = {};
const uint8_t* ies_begin = ie_blob;
const uint8_t* ies_end = ie_blob + ie_blob_len;
const uint8_t* next_ie = ies_begin;
@@ -426,10 +434,11 @@
if (!hidl_scan_result) {
return false;
}
+ *hidl_scan_result = {};
hidl_scan_result->timeStampInUs = legacy_scan_result.ts;
hidl_scan_result->ssid = std::vector<uint8_t>(
legacy_scan_result.ssid,
- legacy_scan_result.ssid + sizeof(legacy_scan_result.ssid));
+ legacy_scan_result.ssid + strlen(legacy_scan_result.ssid));
memcpy(hidl_scan_result->bssid.data(),
legacy_scan_result.bssid,
hidl_scan_result->bssid.size());
@@ -456,6 +465,7 @@
if (!hidl_scan_data) {
return false;
}
+ *hidl_scan_data = {};
hidl_scan_data->flags = 0;
for (const auto flag : {legacy_hal::WIFI_SCAN_FLAG_INTERRUPTED}) {
if (legacy_cached_scan_result.flags & flag) {
@@ -492,7 +502,7 @@
if (!hidl_scan_datas) {
return false;
}
- hidl_scan_datas->clear();
+ *hidl_scan_datas = {};
for (const auto& legacy_cached_scan_result : legacy_cached_scan_results) {
StaScanData hidl_scan_data;
if (!convertLegacyCachedGscanResultsToHidl(legacy_cached_scan_result,
@@ -579,6 +589,7 @@
if (!hidl_frame) {
return false;
}
+ *hidl_frame = {};
hidl_frame->frameType =
convertLegacyDebugPacketFateFrameTypeToHidl(legacy_frame.payload_type);
hidl_frame->frameLen = legacy_frame.frame_len;
@@ -597,6 +608,7 @@
if (!hidl_fate) {
return false;
}
+ *hidl_fate = {};
hidl_fate->fate = convertLegacyDebugTxPacketFateToHidl(legacy_fate.fate);
return convertLegacyDebugPacketFateFrameToHidl(legacy_fate.frame_inf,
&hidl_fate->frameInfo);
@@ -608,7 +620,7 @@
if (!hidl_fates) {
return false;
}
- hidl_fates->clear();
+ *hidl_fates = {};
for (const auto& legacy_fate : legacy_fates) {
WifiDebugTxPacketFateReport hidl_fate;
if (!convertLegacyDebugTxPacketFateToHidl(legacy_fate, &hidl_fate)) {
@@ -625,6 +637,7 @@
if (!hidl_fate) {
return false;
}
+ *hidl_fate = {};
hidl_fate->fate = convertLegacyDebugRxPacketFateToHidl(legacy_fate.fate);
return convertLegacyDebugPacketFateFrameToHidl(legacy_fate.frame_inf,
&hidl_fate->frameInfo);
@@ -636,7 +649,7 @@
if (!hidl_fates) {
return false;
}
- hidl_fates->clear();
+ *hidl_fates = {};
for (const auto& legacy_fate : legacy_fates) {
WifiDebugRxPacketFateReport hidl_fate;
if (!convertLegacyDebugRxPacketFateToHidl(legacy_fate, &hidl_fate)) {
@@ -653,6 +666,7 @@
if (!hidl_stats) {
return false;
}
+ *hidl_stats = {};
// iface legacy_stats conversion.
hidl_stats->iface.beaconRx = legacy_stats.iface.beacon_rx;
hidl_stats->iface.avgRssiMgmt = legacy_stats.iface.rssi_mgmt;
@@ -706,6 +720,7 @@
if (!hidl_caps) {
return false;
}
+ *hidl_caps = {};
hidl_caps->maxBlacklistSize = legacy_caps.max_blacklist_size;
hidl_caps->maxWhitelistSize = legacy_caps.max_whitelist_size;
return true;
@@ -717,6 +732,7 @@
if (!legacy_config) {
return false;
}
+ *legacy_config = {};
if (hidl_config.bssidBlacklist.size() > MAX_BLACKLIST_BSSID ||
hidl_config.ssidWhitelist.size() > MAX_WHITELIST_SSID) {
return false;
@@ -762,7 +778,7 @@
LOG(ERROR) << "convertHidlNanEnableRequestToLegacy: null legacy_request";
return false;
}
- memset(legacy_request, 0, sizeof(legacy_hal::NanEnableRequest));
+ *legacy_request = {};
legacy_request->config_2dot4g_support = 1;
legacy_request->support_2dot4g_val = hidl_request.operateInBand[
@@ -892,7 +908,7 @@
LOG(ERROR) << "convertHidlNanPublishRequestToLegacy: null legacy_request";
return false;
}
- memset(legacy_request, 0, sizeof(legacy_hal::NanPublishRequest));
+ *legacy_request = {};
legacy_request->publish_id = hidl_request.baseConfigs.sessionId;
legacy_request->ttl = hidl_request.baseConfigs.ttlSec;
@@ -984,7 +1000,7 @@
LOG(ERROR) << "convertHidlNanSubscribeRequestToLegacy: legacy_request is null";
return false;
}
- memset(legacy_request, 0, sizeof(legacy_hal::NanSubscribeRequest));
+ *legacy_request = {};
legacy_request->subscribe_id = hidl_request.baseConfigs.sessionId;
legacy_request->ttl = hidl_request.baseConfigs.ttlSec;
@@ -1089,7 +1105,7 @@
LOG(ERROR) << "convertHidlNanTransmitFollowupRequestToLegacy: legacy_request is null";
return false;
}
- memset(legacy_request, 0, sizeof(legacy_hal::NanTransmitFollowupRequest));
+ *legacy_request = {};
legacy_request->publish_subscribe_id = hidl_request.discoverySessionId;
legacy_request->requestor_instance_id = hidl_request.peerId;
@@ -1128,7 +1144,7 @@
LOG(ERROR) << "convertHidlNanConfigRequestToLegacy: legacy_request is null";
return false;
}
- memset(legacy_request, 0, sizeof(legacy_hal::NanConfigRequest));
+ *legacy_request = {};
// TODO: b/34059183 tracks missing configurations in legacy HAL or uknown defaults
legacy_request->master_pref = hidl_request.masterPref;
@@ -1218,7 +1234,7 @@
LOG(ERROR) << "convertHidlNanDataPathInitiatorRequestToLegacy: legacy_request is null";
return false;
}
- memset(legacy_request, 0, sizeof(legacy_hal::NanDataPathInitiatorRequest));
+ *legacy_request = {};
legacy_request->requestor_instance_id = hidl_request.peerId;
memcpy(legacy_request->peer_disc_mac_addr, hidl_request.peerDiscMacAddr.data(), 6);
@@ -1253,7 +1269,7 @@
LOG(ERROR) << "convertHidlNanDataPathIndicationResponseToLegacy: legacy_request is null";
return false;
}
- memset(legacy_request, 0, sizeof(legacy_hal::NanDataPathIndicationResponse));
+ *legacy_request = {};
legacy_request->rsp_code = hidl_request.acceptRequest ?
legacy_hal::NAN_DP_REQUEST_ACCEPT : legacy_hal::NAN_DP_REQUEST_REJECT;
@@ -1286,9 +1302,10 @@
LOG(ERROR) << "convertLegacyNanResponseHeaderToHidl: wifiNanStatus is null";
return false;
}
+ *wifiNanStatus = {};
+
wifiNanStatus->status = convertLegacyNanStatusTypeToHidl(legacy_response.status);
wifiNanStatus->description = legacy_response.nan_error;
-
return true;
}
@@ -1299,6 +1316,8 @@
LOG(ERROR) << "convertLegacyNanCapabilitiesResponseToHidl: hidl_response is null";
return false;
}
+ *hidl_response = {};
+
hidl_response->maxConcurrentClusters = legacy_response.max_concurrent_nan_clusters;
hidl_response->maxPublishes = legacy_response.max_publishes;
hidl_response->maxSubscribes = legacy_response.max_subscribes;
@@ -1325,6 +1344,8 @@
LOG(ERROR) << "convertLegacyNanMatchIndToHidl: hidl_ind is null";
return false;
}
+ *hidl_ind = {};
+
hidl_ind->discoverySessionId = legacy_ind.publish_subscribe_id;
hidl_ind->peerId = legacy_ind.requestor_instance_id;
hidl_ind->addr = hidl_array<uint8_t, 6>(legacy_ind.addr);
@@ -1356,6 +1377,8 @@
LOG(ERROR) << "convertLegacyNanFollowupIndToHidl: hidl_ind is null";
return false;
}
+ *hidl_ind = {};
+
hidl_ind->discoverySessionId = legacy_ind.publish_subscribe_id;
hidl_ind->peerId = legacy_ind.requestor_instance_id;
hidl_ind->addr = hidl_array<uint8_t, 6>(legacy_ind.addr);
@@ -1376,6 +1399,8 @@
LOG(ERROR) << "convertLegacyNanDataPathRequestIndToHidl: hidl_ind is null";
return false;
}
+ *hidl_ind = {};
+
hidl_ind->discoverySessionId = legacy_ind.service_instance_id;
hidl_ind->peerDiscMacAddr = hidl_array<uint8_t, 6>(legacy_ind.peer_disc_mac_addr);
hidl_ind->ndpInstanceId = legacy_ind.ndp_instance_id;
@@ -1394,6 +1419,8 @@
LOG(ERROR) << "convertLegacyNanDataPathConfirmIndToHidl: hidl_ind is null";
return false;
}
+ *hidl_ind = {};
+
hidl_ind->ndpInstanceId = legacy_ind.ndp_instance_id;
hidl_ind->dataPathSetupSuccess = legacy_ind.rsp_code == legacy_hal::NAN_DP_REQUEST_ACCEPT;
hidl_ind->peerNdiMacAddr = hidl_array<uint8_t, 6>(legacy_ind.peer_ndi_mac_addr);
@@ -1635,6 +1662,7 @@
if (!legacy_info) {
return false;
}
+ *legacy_info = {};
legacy_info->width = convertHidlWifiChannelWidthToLegacy(hidl_info.width);
legacy_info->center_freq = hidl_info.centerFreq;
legacy_info->center_freq0 = hidl_info.centerFreq0;
@@ -1648,6 +1676,7 @@
if (!hidl_info) {
return false;
}
+ *hidl_info = {};
hidl_info->width = convertLegacyWifiChannelWidthToHidl(legacy_info.width);
hidl_info->centerFreq = legacy_info.center_freq;
hidl_info->centerFreq0 = legacy_info.center_freq0;
@@ -1660,6 +1689,7 @@
if (!legacy_config) {
return false;
}
+ *legacy_config = {};
CHECK(hidl_config.addr.size() == sizeof(legacy_config->addr));
memcpy(legacy_config->addr, hidl_config.addr.data(), hidl_config.addr.size());
legacy_config->type = convertHidlRttTypeToLegacy(hidl_config.type);
@@ -1688,7 +1718,7 @@
if (!legacy_configs) {
return false;
}
- legacy_configs->clear();
+ *legacy_configs = {};
for (const auto& hidl_config : hidl_configs) {
legacy_hal::wifi_rtt_config legacy_config;
if (!convertHidlRttConfigToLegacy(hidl_config, &legacy_config)) {
@@ -1705,6 +1735,7 @@
if (!legacy_info) {
return false;
}
+ *legacy_info = {};
legacy_info->latitude = hidl_info.latitude;
legacy_info->longitude = hidl_info.longitude;
legacy_info->altitude = hidl_info.altitude;
@@ -1725,6 +1756,7 @@
if (!legacy_info) {
return false;
}
+ *legacy_info = {};
CHECK(hidl_info.countryCode.size() == sizeof(legacy_info->country_code));
memcpy(legacy_info->country_code,
hidl_info.countryCode.data(),
@@ -1745,6 +1777,7 @@
if (!legacy_responder) {
return false;
}
+ *legacy_responder = {};
if (!convertHidlWifiChannelInfoToLegacy(hidl_responder.channel,
&legacy_responder->channel)) {
return false;
@@ -1760,6 +1793,7 @@
if (!hidl_responder) {
return false;
}
+ *hidl_responder = {};
if (!convertLegacyWifiChannelInfoToHidl(legacy_responder.channel,
&hidl_responder->channel)) {
return false;
@@ -1775,6 +1809,7 @@
if (!hidl_capabilities) {
return false;
}
+ *hidl_capabilities = {};
hidl_capabilities->rttOneSidedSupported =
legacy_capabilities.rtt_one_sided_supported;
hidl_capabilities->rttFtmSupported = legacy_capabilities.rtt_ftm_supported;
@@ -1814,6 +1849,7 @@
if (!hidl_rate) {
return false;
}
+ *hidl_rate = {};
hidl_rate->preamble =
convertLegacyWifiRatePreambleToHidl(legacy_rate.preamble);
hidl_rate->nss = convertLegacyWifiRateNssToHidl(legacy_rate.nss);
@@ -1829,6 +1865,7 @@
if (!hidl_result) {
return false;
}
+ *hidl_result = {};
CHECK(sizeof(legacy_result.addr) == hidl_result->addr.size());
memcpy(
hidl_result->addr.data(), legacy_result.addr, sizeof(legacy_result.addr));
@@ -1873,7 +1910,7 @@
if (!hidl_results) {
return false;
}
- hidl_results->clear();
+ *hidl_results = {};
for (const auto legacy_result : legacy_results) {
RttResult hidl_result;
if (!convertLegacyRttResultToHidl(*legacy_result, &hidl_result)) {
diff --git a/wifi/1.0/vts/functional/wifi_hidl_test_utils.cpp b/wifi/1.0/vts/functional/wifi_hidl_test_utils.cpp
index 9042075..e0c92fe 100644
--- a/wifi/1.0/vts/functional/wifi_hidl_test_utils.cpp
+++ b/wifi/1.0/vts/functional/wifi_hidl_test_utils.cpp
@@ -36,11 +36,12 @@
using ::android::hardware::hidl_vec;
void stopFramework() {
- ASSERT_EQ(std::system("svc wifi disable"), 0);
+ ASSERT_EQ(std::system("stop"), 0);
+ stopWifi();
sleep(5);
}
-void startFramework() { ASSERT_EQ(std::system("svc wifi enable"), 0); }
+void startFramework() { ASSERT_EQ(std::system("start"), 0); }
sp<IWifi> getWifi() {
sp<IWifi> wifi = ::testing::VtsHalHidlTargetBaseTest::getService<IWifi>();
diff --git a/wifi/1.0/vts/functional/wifi_nan_iface_hidl_test.cpp b/wifi/1.0/vts/functional/wifi_nan_iface_hidl_test.cpp
index eb482c9..95c0e5d 100644
--- a/wifi/1.0/vts/functional/wifi_nan_iface_hidl_test.cpp
+++ b/wifi/1.0/vts/functional/wifi_nan_iface_hidl_test.cpp
@@ -17,24 +17,427 @@
#include <android-base/logging.h>
#include <android/hardware/wifi/1.0/IWifiNanIface.h>
+#include <android/hardware/wifi/1.0/IWifiNanIfaceEventCallback.h>
#include <VtsHalHidlTargetBaseTest.h>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include "wifi_hidl_call_util.h"
#include "wifi_hidl_test_utils.h"
-using ::android::hardware::wifi::V1_0::IWifiNanIface;
+using namespace ::android::hardware::wifi::V1_0;
+
+using ::android::hardware::Return;
+using ::android::hardware::Void;
using ::android::sp;
+#define TIMEOUT_PERIOD 10
+
/**
* Fixture to use for all NAN Iface HIDL interface tests.
*/
class WifiNanIfaceHidlTest : public ::testing::VtsHalHidlTargetBaseTest {
- public:
- virtual void SetUp() override {}
+ public:
+ virtual void SetUp() override {
+ iwifiNanIface = getWifiNanIface();
+ ASSERT_NE(nullptr, iwifiNanIface.get());
+ ASSERT_EQ(WifiStatusCode::SUCCESS, HIDL_INVOKE(iwifiNanIface, registerEventCallback,
+ new WifiNanIfaceEventCallback(*this)).code);
+ }
- virtual void TearDown() override { stopWifi(); }
+ virtual void TearDown() override {
+ stopWifi();
+ }
- protected:
+ /* Used as a mechanism to inform the test about data/event callback */
+ inline void notify() {
+ std::unique_lock<std::mutex> lock(mtx_);
+ count_++;
+ cv_.notify_one();
+ }
+
+ enum CallbackType {
+ INVALID = -2,
+ ANY_CALLBACK = -1,
+
+ NOTIFY_CAPABILITIES_RESPONSE = 0,
+ NOTIFY_ENABLE_RESPONSE,
+ NOTIFY_CONFIG_RESPONSE,
+ NOTIFY_DISABLE_RESPONSE,
+ NOTIFY_START_PUBLISH_RESPONSE,
+ NOTIFY_STOP_PUBLISH_RESPONSE,
+ NOTIFY_START_SUBSCRIBE_RESPONSE,
+ NOTIFY_STOP_SUBSCRIBE_RESPONSE,
+ NOTIFY_TRANSMIT_FOLLOWUP_RESPONSE,
+ NOTIFY_CREATE_DATA_INTERFACE_RESPONSE,
+ NOTIFY_DELETE_DATA_INTERFACE_RESPONSE,
+ NOTIFY_INITIATE_DATA_PATH_RESPONSE,
+ NOTIFY_RESPOND_TO_DATA_PATH_INDICATION_RESPONSE,
+ NOTIFY_TERMINATE_DATA_PATH_RESPONSE,
+
+ EVENT_CLUSTER_EVENT,
+ EVENT_DISABLED,
+ EVENT_PUBLISH_TERMINATED,
+ EVENT_SUBSCRIBE_TERMINATED,
+ EVENT_MATCH,
+ EVENT_MATCH_EXPIRED,
+ EVENT_FOLLOWUP_RECEIVED,
+ EVENT_TRANSMIT_FOLLOWUP,
+ EVENT_DATA_PATH_REQUEST,
+ EVENT_DATA_PATH_CONFIRM,
+ EVENT_DATA_PATH_TERMINATED
+ };
+
+ /* Test code calls this function to wait for data/event callback */
+ inline std::cv_status wait(CallbackType waitForCallbackType) {
+ std::unique_lock<std::mutex> lock(mtx_);
+
+ EXPECT_NE(INVALID, waitForCallbackType); // can't ASSERT in a non-void-returning method
+
+ callbackType = INVALID;
+ std::cv_status status = std::cv_status::no_timeout;
+ auto now = std::chrono::system_clock::now();
+ while (count_ == 0) {
+ status = cv_.wait_until(lock, now + std::chrono::seconds(TIMEOUT_PERIOD));
+ if (status == std::cv_status::timeout) return status;
+ if (waitForCallbackType != ANY_CALLBACK && callbackType != INVALID
+ && callbackType != waitForCallbackType) {
+ count_--;
+ }
+ }
+ count_--;
+ return status;
+ }
+
+ class WifiNanIfaceEventCallback: public IWifiNanIfaceEventCallback {
+ WifiNanIfaceHidlTest& parent_;
+
+ public:
+ WifiNanIfaceEventCallback(WifiNanIfaceHidlTest& parent) : parent_(parent) {};
+
+ virtual ~WifiNanIfaceEventCallback() = default;
+
+ Return<void> notifyCapabilitiesResponse(
+ uint16_t id,
+ const WifiNanStatus& status,
+ const NanCapabilities& capabilities) override {
+ parent_.callbackType = NOTIFY_CAPABILITIES_RESPONSE;
+
+ parent_.id = id;
+ parent_.status = status;
+ parent_.capabilities = capabilities;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> notifyEnableResponse(
+ uint16_t id,
+ const WifiNanStatus& status) override {
+ parent_.callbackType = NOTIFY_ENABLE_RESPONSE;
+
+ parent_.id = id;
+ parent_.status = status;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> notifyConfigResponse(
+ uint16_t id,
+ const WifiNanStatus& status) override {
+ parent_.callbackType = NOTIFY_CONFIG_RESPONSE;
+
+ parent_.id = id;
+ parent_.status = status;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> notifyDisableResponse(
+ uint16_t id,
+ const WifiNanStatus& status) override {
+ parent_.callbackType = NOTIFY_DISABLE_RESPONSE;
+
+ parent_.id = id;
+ parent_.status = status;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> notifyStartPublishResponse(
+ uint16_t id,
+ const WifiNanStatus& status,
+ uint8_t sessionId) override {
+ parent_.callbackType = NOTIFY_START_PUBLISH_RESPONSE;
+
+ parent_.id = id;
+ parent_.status = status;
+ parent_.sessionId = sessionId;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> notifyStopPublishResponse(
+ uint16_t id,
+ const WifiNanStatus& status) override {
+ parent_.callbackType = NOTIFY_STOP_PUBLISH_RESPONSE;
+
+ parent_.id = id;
+ parent_.status = status;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> notifyStartSubscribeResponse(
+ uint16_t id,
+ const WifiNanStatus& status,
+ uint8_t sessionId) override {
+ parent_.callbackType = NOTIFY_START_SUBSCRIBE_RESPONSE;
+
+ parent_.id = id;
+ parent_.status = status;
+ parent_.sessionId = sessionId;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> notifyStopSubscribeResponse(
+ uint16_t id,
+ const WifiNanStatus& status) override {
+ parent_.callbackType = NOTIFY_STOP_SUBSCRIBE_RESPONSE;
+
+ parent_.id = id;
+ parent_.status = status;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> notifyTransmitFollowupResponse(
+ uint16_t id,
+ const WifiNanStatus& status) override {
+ parent_.callbackType = NOTIFY_TRANSMIT_FOLLOWUP_RESPONSE;
+
+ parent_.id = id;
+ parent_.status = status;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> notifyCreateDataInterfaceResponse(
+ uint16_t id,
+ const WifiNanStatus& status) override {
+ parent_.callbackType = NOTIFY_CREATE_DATA_INTERFACE_RESPONSE;
+
+ parent_.id = id;
+ parent_.status = status;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> notifyDeleteDataInterfaceResponse(
+ uint16_t id,
+ const WifiNanStatus& status) override {
+ parent_.callbackType = NOTIFY_DELETE_DATA_INTERFACE_RESPONSE;
+
+ parent_.id = id;
+ parent_.status = status;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> notifyInitiateDataPathResponse(
+ uint16_t id,
+ const WifiNanStatus& status,
+ uint32_t ndpInstanceId) override {
+ parent_.callbackType = NOTIFY_INITIATE_DATA_PATH_RESPONSE;
+
+ parent_.id = id;
+ parent_.status = status;
+ parent_.ndpInstanceId = ndpInstanceId;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> notifyRespondToDataPathIndicationResponse(
+ uint16_t id,
+ const WifiNanStatus& status) override {
+ parent_.callbackType = NOTIFY_RESPOND_TO_DATA_PATH_INDICATION_RESPONSE;
+
+ parent_.id = id;
+ parent_.status = status;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> notifyTerminateDataPathResponse(
+ uint16_t id,
+ const WifiNanStatus& status) override {
+ parent_.callbackType = NOTIFY_TERMINATE_DATA_PATH_RESPONSE;
+
+ parent_.id = id;
+ parent_.status = status;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> eventClusterEvent(
+ const NanClusterEventInd& event) override {
+ parent_.callbackType = EVENT_CLUSTER_EVENT;
+
+ parent_.nanClusterEventInd = event;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> eventDisabled(
+ const WifiNanStatus& status) override {
+ parent_.callbackType = EVENT_DISABLED;
+
+ parent_.status = status;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> eventPublishTerminated(
+ uint8_t sessionId,
+ const WifiNanStatus& status) override {
+ parent_.callbackType = EVENT_PUBLISH_TERMINATED;
+
+ parent_.sessionId = sessionId;
+ parent_.status = status;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> eventSubscribeTerminated(
+ uint8_t sessionId,
+ const WifiNanStatus& status) override {
+ parent_.callbackType = EVENT_SUBSCRIBE_TERMINATED;
+
+ parent_.sessionId = sessionId;
+ parent_.status = status;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> eventMatch(
+ const NanMatchInd& event) override {
+ parent_.callbackType = EVENT_MATCH;
+
+ parent_.nanMatchInd = event;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> eventMatchExpired(
+ uint8_t discoverySessionId,
+ uint32_t peerId) override {
+ parent_.callbackType = EVENT_MATCH_EXPIRED;
+
+ parent_.sessionId = discoverySessionId;
+ parent_.peerId = peerId;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> eventFollowupReceived(
+ const NanFollowupReceivedInd& event) override {
+ parent_.callbackType = EVENT_FOLLOWUP_RECEIVED;
+
+ parent_.nanFollowupReceivedInd = event;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> eventTransmitFollowup(
+ uint16_t id,
+ const WifiNanStatus& status) override {
+ parent_.callbackType = EVENT_TRANSMIT_FOLLOWUP;
+
+ parent_.id = id;
+ parent_.status = status;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> eventDataPathRequest(
+ const NanDataPathRequestInd& event) override {
+ parent_.callbackType = EVENT_DATA_PATH_REQUEST;
+
+ parent_.nanDataPathRequestInd = event;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> eventDataPathConfirm(
+ const NanDataPathConfirmInd& event) override {
+ parent_.callbackType = EVENT_DATA_PATH_CONFIRM;
+
+ parent_.nanDataPathConfirmInd = event;
+
+ parent_.notify();
+ return Void();
+ }
+
+ Return<void> eventDataPathTerminated(
+ uint32_t ndpInstanceId) override {
+ parent_.callbackType = EVENT_DATA_PATH_TERMINATED;
+
+ parent_.ndpInstanceId = ndpInstanceId;
+
+ parent_.notify();
+ return Void();
+ }
+ };
+
+ private:
+ // synchronization objects
+ std::mutex mtx_;
+ std::condition_variable cv_;
+ int count_;
+
+ protected:
+ android::sp<IWifiNanIface> iwifiNanIface;
+
+ // Data from IWifiNanIfaceEventCallback callbacks: this is the collection of all
+ // arguments to all callbacks. They are set by the callback (notifications or
+ // events) and can be retrieved by tests.
+ CallbackType callbackType;
+ uint16_t id;
+ WifiNanStatus status;
+ NanCapabilities capabilities;
+ uint8_t sessionId;
+ uint32_t ndpInstanceId;
+ NanClusterEventInd nanClusterEventInd;
+ NanMatchInd nanMatchInd;
+ uint32_t peerId;
+ NanFollowupReceivedInd nanFollowupReceivedInd;
+ NanDataPathRequestInd nanDataPathRequestInd;
+ NanDataPathConfirmInd nanDataPathConfirmInd;
};
/*
@@ -43,6 +446,49 @@
* successfully created.
*/
TEST(WifiNanIfaceHidlTestNoFixture, Create) {
- EXPECT_NE(nullptr, getWifiNanIface().get());
- stopWifi();
+ ASSERT_NE(nullptr, getWifiNanIface().get());
+ stopWifi();
+}
+
+/*
+ * Fail: use past destruction
+ * Ensure that API calls fail with ERROR_WIFI_IFACE_INVALID when using an interface once wifi
+ * is disabled.
+ */
+TEST(WifiNanIfaceHidlTestNoFixture, FailOnIfaceInvalid) {
+ android::sp<IWifiNanIface> iwifiNanIface = getWifiNanIface();
+ ASSERT_NE(nullptr, iwifiNanIface.get());
+ stopWifi();
+ sleep(5); // make sure that all chips/interfaces are invalidated
+ ASSERT_EQ(WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ HIDL_INVOKE(iwifiNanIface, getCapabilitiesRequest, 0).code);
+}
+
+/*
+ * getCapabilitiesRequest: validate that returns capabilities.
+ */
+TEST_F(WifiNanIfaceHidlTest, getCapabilitiesRequest) {
+ uint16_t inputCmdId = 10;
+ ASSERT_EQ(WifiStatusCode::SUCCESS,
+ HIDL_INVOKE(iwifiNanIface, getCapabilitiesRequest, inputCmdId).code);
+ // wait for a callback
+ ASSERT_EQ(std::cv_status::no_timeout, wait(NOTIFY_CAPABILITIES_RESPONSE));
+ ASSERT_EQ(NOTIFY_CAPABILITIES_RESPONSE, callbackType);
+ ASSERT_EQ(id, inputCmdId);
+
+ // check for reasonable capability values
+ EXPECT_GT(capabilities.maxConcurrentClusters, (unsigned int) 0);
+ EXPECT_GT(capabilities.maxPublishes, (unsigned int) 0);
+ EXPECT_GT(capabilities.maxSubscribes, (unsigned int) 0);
+ EXPECT_EQ(capabilities.maxServiceNameLen, (unsigned int) 255);
+ EXPECT_EQ(capabilities.maxMatchFilterLen, (unsigned int) 255);
+ EXPECT_GT(capabilities.maxTotalMatchFilterLen, (unsigned int) 255);
+ EXPECT_EQ(capabilities.maxServiceSpecificInfoLen, (unsigned int) 255);
+ EXPECT_GE(capabilities.maxExtendedServiceSpecificInfoLen, (unsigned int) 255);
+ EXPECT_GT(capabilities.maxNdiInterfaces, (unsigned int) 0);
+ EXPECT_GT(capabilities.maxNdpSessions, (unsigned int) 0);
+ EXPECT_GT(capabilities.maxAppInfoLen, (unsigned int) 0);
+ EXPECT_GT(capabilities.maxQueuedTransmitFollowupMsgs, (unsigned int) 0);
+ EXPECT_GT(capabilities.maxSubscribeInterfaceAddresses, (unsigned int) 0);
+ EXPECT_NE(capabilities.supportedCipherSuites, (unsigned int) 0);
}