mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 11:36:00 +00:00
Merge "DO NOT MERGE - Merge Android 13"
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.vscode/
|
||||
@@ -33,7 +33,7 @@ aidl_interface {
|
||||
"android/hardware/audio/common/SourceMetadata.aidl",
|
||||
],
|
||||
imports: [
|
||||
"android.media.audio.common.types",
|
||||
"android.media.audio.common.types-V1",
|
||||
],
|
||||
stability: "vintf",
|
||||
backend: {
|
||||
@@ -41,7 +41,12 @@ aidl_interface {
|
||||
enabled: true,
|
||||
},
|
||||
java: {
|
||||
platform_apis: true,
|
||||
sdk_version: "module_current",
|
||||
min_sdk_version: "31",
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
"com.android.car.framework",
|
||||
],
|
||||
},
|
||||
ndk: {
|
||||
apex_available: [
|
||||
@@ -51,6 +56,11 @@ aidl_interface {
|
||||
min_sdk_version: "31",
|
||||
},
|
||||
},
|
||||
versions: [
|
||||
versions_with_info: [
|
||||
{
|
||||
version: "1",
|
||||
imports: ["android.media.audio.common.types-V1"],
|
||||
},
|
||||
],
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
90d0a7ea5cee4579d33066885d8648f180387f55
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.audio.common;
|
||||
@JavaDerive(equals=true, toString=true) @VintfStability
|
||||
parcelable PlaybackTrackMetadata {
|
||||
android.media.audio.common.AudioUsage usage = android.media.audio.common.AudioUsage.INVALID;
|
||||
android.media.audio.common.AudioContentType contentType = android.media.audio.common.AudioContentType.UNKNOWN;
|
||||
float gain;
|
||||
android.media.audio.common.AudioChannelLayout channelMask;
|
||||
@nullable android.media.audio.common.AudioDevice sourceDevice;
|
||||
@utf8InCpp String[] tags;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.audio.common;
|
||||
@JavaDerive(equals=true, toString=true) @VintfStability
|
||||
parcelable RecordTrackMetadata {
|
||||
android.media.audio.common.AudioSource source = android.media.audio.common.AudioSource.SYS_RESERVED_INVALID;
|
||||
float gain;
|
||||
@nullable android.media.audio.common.AudioDevice destinationDevice;
|
||||
android.media.audio.common.AudioChannelLayout channelMask;
|
||||
@utf8InCpp String[] tags;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.audio.common;
|
||||
@JavaDerive(equals=true, toString=true) @VintfStability
|
||||
parcelable SinkMetadata {
|
||||
android.hardware.audio.common.RecordTrackMetadata[] tracks;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.audio.common;
|
||||
@JavaDerive(equals=true, toString=true) @VintfStability
|
||||
parcelable SourceMetadata {
|
||||
android.hardware.audio.common.PlaybackTrackMetadata[] tracks;
|
||||
}
|
||||
@@ -37,5 +37,7 @@ parcelable PlaybackTrackMetadata {
|
||||
android.media.audio.common.AudioUsage usage = android.media.audio.common.AudioUsage.INVALID;
|
||||
android.media.audio.common.AudioContentType contentType = android.media.audio.common.AudioContentType.UNKNOWN;
|
||||
float gain;
|
||||
android.media.audio.common.AudioChannelLayout channelMask;
|
||||
@nullable android.media.audio.common.AudioDevice sourceDevice;
|
||||
@utf8InCpp String[] tags;
|
||||
}
|
||||
|
||||
@@ -36,5 +36,7 @@ package android.hardware.audio.common;
|
||||
parcelable RecordTrackMetadata {
|
||||
android.media.audio.common.AudioSource source = android.media.audio.common.AudioSource.SYS_RESERVED_INVALID;
|
||||
float gain;
|
||||
@nullable android.media.audio.common.AudioDevice destinationDevice;
|
||||
android.media.audio.common.AudioChannelLayout channelMask;
|
||||
@utf8InCpp String[] tags;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
package android.hardware.audio.common;
|
||||
|
||||
import android.media.audio.common.AudioChannelLayout;
|
||||
import android.media.audio.common.AudioContentType;
|
||||
import android.media.audio.common.AudioDevice;
|
||||
import android.media.audio.common.AudioUsage;
|
||||
|
||||
/**
|
||||
@@ -32,6 +34,11 @@ parcelable PlaybackTrackMetadata {
|
||||
* 0 means muted, 1 is unity gain, 2 means double amplitude, etc.
|
||||
*/
|
||||
float gain;
|
||||
AudioChannelLayout channelMask;
|
||||
/**
|
||||
* Indicates the source of an output stream, can be left unspecified.
|
||||
*/
|
||||
@nullable AudioDevice sourceDevice;
|
||||
/**
|
||||
* Tags from AudioTrack audio attributes. Tag is an additional use case
|
||||
* qualifier complementing AudioUsage and AudioContentType. Tags are set by
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package android.hardware.audio.common;
|
||||
|
||||
import android.media.audio.common.AudioChannelLayout;
|
||||
import android.media.audio.common.AudioContentType;
|
||||
import android.media.audio.common.AudioDevice;
|
||||
import android.media.audio.common.AudioSource;
|
||||
|
||||
/**
|
||||
@@ -30,6 +33,11 @@ parcelable RecordTrackMetadata {
|
||||
* 0 means muted, 1 is unity gain, 2 means double amplitude, etc.
|
||||
*/
|
||||
float gain;
|
||||
/**
|
||||
* Indicates the destination of an input stream, can be left unspecified.
|
||||
*/
|
||||
@nullable AudioDevice destinationDevice;
|
||||
AudioChannelLayout channelMask;
|
||||
/**
|
||||
* Tags from AudioRecord audio attributes. Tag is an additional use case
|
||||
* qualifier complementing AudioUsage and AudioContentType. Tags are set by
|
||||
|
||||
@@ -20,4 +20,8 @@ hidl_interface {
|
||||
],
|
||||
gen_java: true,
|
||||
gen_java_constants: true,
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
"com.android.bluetooth",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -20,4 +20,8 @@ hidl_interface {
|
||||
],
|
||||
gen_java: true,
|
||||
gen_java_constants: true,
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
"com.android.car.framework",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -47,3 +47,57 @@ TEST_P(AudioHidlDeviceTest, SetConnectedState_7_1) {
|
||||
// initial state. To workaround this, destroy the HAL at the end of this test.
|
||||
ASSERT_TRUE(resetDevice());
|
||||
}
|
||||
|
||||
class LatencyModeOutputStreamTest : public OutputStreamTest {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
OutputStreamTest::SetUp();
|
||||
|
||||
Result res;
|
||||
EXPECT_OK(stream->getRecommendedLatencyModes(returnIn(res, mSupportedLatencyModes)));
|
||||
EXPECT_RESULT(okOrNotSupported, res);
|
||||
if (res == Result::NOT_SUPPORTED) {
|
||||
GTEST_SKIP() << "latency mode is not supported"; // returns
|
||||
}
|
||||
}
|
||||
hidl_vec<LatencyMode> mSupportedLatencyModes;
|
||||
};
|
||||
|
||||
TEST_P(LatencyModeOutputStreamTest, GetRecommendedLatencyModes) {
|
||||
doc::test("Verify that reported latency modes are valid when supported");
|
||||
for (auto mode : mSupportedLatencyModes) {
|
||||
ASSERT_TRUE(mode >= LatencyMode::FREE && mode <= LatencyMode::LOW);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(LatencyModeOutputStreamTest, SetValidLatencyMode) {
|
||||
doc::test("Verify that setting valid latency modes works when supported");
|
||||
for (auto mode : mSupportedLatencyModes) {
|
||||
EXPECT_OK(stream->setLatencyMode(mode));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(LatencyModeOutputStreamTest, SetInValidLatencyMode) {
|
||||
doc::test("Verify that setting invalid latency modes fails");
|
||||
EXPECT_RESULT(invalidArgsOrNotSupported,
|
||||
stream->setLatencyMode(static_cast<LatencyMode>(1977)));
|
||||
}
|
||||
|
||||
/** Stub implementation of IStreamOutEventCallback **/
|
||||
class MockOutLatencyModeCallback : public IStreamOutLatencyModeCallback {
|
||||
Return<void> onRecommendedLatencyModeChanged(
|
||||
const hidl_vec<LatencyMode>& hidlModes __unused) override {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(LatencyModeOutputStreamTest, SetLatencyModeCallback) {
|
||||
doc::test("Verify that setting a latency mode callback works when supported");
|
||||
EXPECT_OK(stream->setLatencyModeCallback(new MockOutLatencyModeCallback));
|
||||
EXPECT_OK(stream->setLatencyModeCallback(nullptr));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(LatencyModeOutputStream, LatencyModeOutputStreamTest,
|
||||
::testing::ValuesIn(getOutputDeviceSingleConfigParameters()),
|
||||
&DeviceConfigParameterToString);
|
||||
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(LatencyModeOutputStreamTest);
|
||||
|
||||
@@ -30,6 +30,7 @@ cc_defaults {
|
||||
"android.hardware.audio.common.test.utility",
|
||||
"audioclient-types-aidl-cpp",
|
||||
"libaudioclient_aidl_conversion",
|
||||
"libstagefright_foundation",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbinder",
|
||||
@@ -60,6 +61,7 @@ cc_test {
|
||||
"libmedia_helper",
|
||||
"android.hardware.audio@2.0",
|
||||
"android.hardware.audio.common@2.0",
|
||||
"android.media.audio.common.types-V1-cpp",
|
||||
],
|
||||
cflags: [
|
||||
"-DMAJOR_VERSION=2",
|
||||
@@ -89,6 +91,7 @@ cc_test {
|
||||
"libmedia_helper",
|
||||
"android.hardware.audio@4.0",
|
||||
"android.hardware.audio.common@4.0",
|
||||
"android.media.audio.common.types-V1-cpp",
|
||||
],
|
||||
cflags: [
|
||||
"-DMAJOR_VERSION=4",
|
||||
@@ -115,6 +118,7 @@ cc_test {
|
||||
"libmedia_helper",
|
||||
"android.hardware.audio@5.0",
|
||||
"android.hardware.audio.common@5.0",
|
||||
"android.media.audio.common.types-V1-cpp",
|
||||
],
|
||||
cflags: [
|
||||
"-DMAJOR_VERSION=5",
|
||||
@@ -145,6 +149,7 @@ cc_test {
|
||||
"libmedia_helper",
|
||||
"android.hardware.audio@6.0",
|
||||
"android.hardware.audio.common@6.0",
|
||||
"android.media.audio.common.types-V1-cpp",
|
||||
],
|
||||
cflags: [
|
||||
"-DMAJOR_VERSION=6",
|
||||
@@ -244,6 +249,7 @@ cc_test {
|
||||
static_libs: [
|
||||
"android.hardware.audio@6.0",
|
||||
"android.hardware.audio.common@6.0",
|
||||
"android.media.audio.common.types-V1-cpp",
|
||||
"libaudiofoundation",
|
||||
"libaudiopolicycomponents",
|
||||
"libmedia_helper",
|
||||
|
||||
@@ -1041,6 +1041,7 @@ class StreamWriter : public StreamWorker<StreamWriter> {
|
||||
|
||||
class OutputStreamTest
|
||||
: public OpenStreamTest<::android::hardware::audio::CPP_VERSION::IStreamOut> {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp()); // setup base
|
||||
#if MAJOR_VERSION <= 6
|
||||
@@ -1066,7 +1067,6 @@ class OutputStreamTest
|
||||
}
|
||||
#if MAJOR_VERSION >= 4 && MAJOR_VERSION <= 6
|
||||
|
||||
protected:
|
||||
const SourceMetadata initMetadata = {
|
||||
{ { AudioUsage::MEDIA,
|
||||
AudioContentType::MUSIC,
|
||||
|
||||
@@ -30,6 +30,7 @@ cc_defaults {
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libaudioutils",
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"libeffects",
|
||||
@@ -48,6 +49,7 @@ cc_defaults {
|
||||
"libeffects_headers",
|
||||
"libhardware_headers",
|
||||
"libmedia_headers",
|
||||
"libmediautils_headers",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -22,13 +22,11 @@
|
||||
#include "Effect.h"
|
||||
#include "common/all-versions/default/EffectMap.h"
|
||||
|
||||
#include <memory.h>
|
||||
|
||||
#define ATRACE_TAG ATRACE_TAG_AUDIO
|
||||
|
||||
#include <HidlUtils.h>
|
||||
#include <android/log.h>
|
||||
#include <media/EffectsFactoryApi.h>
|
||||
#include <mediautils/ScopedStatistics.h>
|
||||
#include <util/EffectUtils.h>
|
||||
#include <utils/Trace.h>
|
||||
|
||||
@@ -49,21 +47,27 @@ using ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION::implementati
|
||||
|
||||
namespace {
|
||||
|
||||
#define SCOPED_STATS() \
|
||||
::android::mediautils::ScopedStatistics scopedStatistics { \
|
||||
std::string("EffectHal::").append(__func__), mEffectHal->mStatistics \
|
||||
}
|
||||
|
||||
class ProcessThread : public Thread {
|
||||
public:
|
||||
// ProcessThread's lifespan never exceeds Effect's lifespan.
|
||||
ProcessThread(std::atomic<bool>* stop, effect_handle_t effect,
|
||||
std::atomic<audio_buffer_t*>* inBuffer, std::atomic<audio_buffer_t*>* outBuffer,
|
||||
Effect::StatusMQ* statusMQ, EventFlag* efGroup)
|
||||
: Thread(false /*canCallJava*/),
|
||||
mStop(stop),
|
||||
mEffect(effect),
|
||||
mHasProcessReverse((*mEffect)->process_reverse != NULL),
|
||||
mInBuffer(inBuffer),
|
||||
mOutBuffer(outBuffer),
|
||||
mStatusMQ(statusMQ),
|
||||
mEfGroup(efGroup) {}
|
||||
virtual ~ProcessThread() {}
|
||||
ProcessThread(std::atomic<bool>* stop, effect_handle_t effect,
|
||||
std::atomic<audio_buffer_t*>* inBuffer, std::atomic<audio_buffer_t*>* outBuffer,
|
||||
Effect::StatusMQ* statusMQ, EventFlag* efGroup, Effect* effectHal)
|
||||
: Thread(false /*canCallJava*/),
|
||||
mStop(stop),
|
||||
mEffect(effect),
|
||||
mHasProcessReverse((*mEffect)->process_reverse != NULL),
|
||||
mInBuffer(inBuffer),
|
||||
mOutBuffer(outBuffer),
|
||||
mStatusMQ(statusMQ),
|
||||
mEfGroup(efGroup),
|
||||
mEffectHal(effectHal) {}
|
||||
virtual ~ProcessThread() {}
|
||||
|
||||
private:
|
||||
std::atomic<bool>* mStop;
|
||||
@@ -73,6 +77,7 @@ class ProcessThread : public Thread {
|
||||
std::atomic<audio_buffer_t*>* mOutBuffer;
|
||||
Effect::StatusMQ* mStatusMQ;
|
||||
EventFlag* mEfGroup;
|
||||
Effect* const mEffectHal;
|
||||
|
||||
bool threadLoop() override;
|
||||
};
|
||||
@@ -102,6 +107,9 @@ bool ProcessThread::threadLoop() {
|
||||
audio_buffer_t* outBuffer =
|
||||
std::atomic_load_explicit(mOutBuffer, std::memory_order_relaxed);
|
||||
if (inBuffer != nullptr && outBuffer != nullptr) {
|
||||
// Time this effect process
|
||||
SCOPED_STATS();
|
||||
|
||||
if (efState & static_cast<uint32_t>(MessageQueueFlagBits::REQUEST_PROCESS)) {
|
||||
processResult = (*mEffect)->process(mEffect, inBuffer, outBuffer);
|
||||
} else {
|
||||
@@ -359,7 +367,7 @@ Return<void> Effect::prepareForProcessing(prepareForProcessing_cb _hidl_cb) {
|
||||
|
||||
// Create and launch the thread.
|
||||
mProcessThread = new ProcessThread(&mStopProcessThread, mHandle, &mHalInBufferPtr,
|
||||
&mHalOutBufferPtr, tempStatusMQ.get(), mEfGroup);
|
||||
&mHalOutBufferPtr, tempStatusMQ.get(), mEfGroup, this);
|
||||
status = mProcessThread->run("effect", PRIORITY_URGENT_AUDIO);
|
||||
if (status != OK) {
|
||||
ALOGW("failed to start effect processing thread: %s", strerror(-status));
|
||||
@@ -749,6 +757,8 @@ Return<void> Effect::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& /
|
||||
if (fd.getNativeHandle() != nullptr && fd->numFds == 1) {
|
||||
uint32_t cmdData = fd->data[0];
|
||||
(void)sendCommand(EFFECT_CMD_DUMP, "DUMP", sizeof(cmdData), &cmdData);
|
||||
const std::string s = mStatistics->dump();
|
||||
if (s.size() != 0) write(cmdData, s.c_str(), s.size());
|
||||
}
|
||||
return Void();
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <fmq/MessageQueue.h>
|
||||
#include <hidl/MQDescriptor.h>
|
||||
#include <hidl/Status.h>
|
||||
#include <mediautils/MethodStatistics.h>
|
||||
#include <utils/Thread.h>
|
||||
|
||||
#include <hardware/audio_effect.h>
|
||||
@@ -169,7 +170,11 @@ struct Effect : public IEffect {
|
||||
Result setParameterImpl(uint32_t paramSize, const void* paramData, uint32_t valueSize,
|
||||
const void* valueData);
|
||||
|
||||
private:
|
||||
// process execution statistics
|
||||
const std::shared_ptr<mediautils::MethodStatistics<std::string>> mStatistics =
|
||||
std::make_shared<mediautils::MethodStatistics<std::string>>();
|
||||
|
||||
private:
|
||||
friend struct VirtualizerEffect; // for getParameterImpl
|
||||
friend struct VisualizerEffect; // to allow executing commands
|
||||
|
||||
|
||||
28
automotive/TEST_MAPPING
Normal file
28
automotive/TEST_MAPPING
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"auto-presubmit": [
|
||||
{
|
||||
"name": "AndroidCarApiTest"
|
||||
},
|
||||
{
|
||||
"name": "CarSecurityPermissionTest"
|
||||
},
|
||||
{
|
||||
"name": "CtsCarTestCases"
|
||||
},
|
||||
{
|
||||
"name": "CtsCarBuiltinApiTestCases"
|
||||
},
|
||||
{
|
||||
"name": "CtsCarHostTestCases"
|
||||
},
|
||||
{
|
||||
"name": "CtsCarBuiltinApiHostTestCases"
|
||||
},
|
||||
{
|
||||
"name": "CarServiceTest"
|
||||
},
|
||||
{
|
||||
"name": "CarServiceUnitTest"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -20,4 +20,8 @@ hidl_interface {
|
||||
"android.hidl.base@1.0",
|
||||
],
|
||||
gen_java: true,
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
"com.android.car.framework",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -24,4 +24,8 @@ hidl_interface {
|
||||
"android.hidl.safe_union@1.0",
|
||||
],
|
||||
gen_java: true,
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
"com.android.car.framework",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -14,16 +14,36 @@ aidl_interface {
|
||||
vendor_available: true,
|
||||
srcs: ["android/hardware/automotive/audiocontrol/*.aidl"],
|
||||
imports: [
|
||||
"android.media.audio.common.types",
|
||||
"android.hardware.audio.common",
|
||||
"android.hardware.audio.common-V1",
|
||||
"android.media.audio.common.types-V1",
|
||||
],
|
||||
stability: "vintf",
|
||||
backend: {
|
||||
java: {
|
||||
platform_apis: true,
|
||||
sdk_version: "module_current",
|
||||
min_sdk_version: "31",
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
"com.android.car.framework",
|
||||
],
|
||||
},
|
||||
},
|
||||
versions: [
|
||||
"1",
|
||||
versions_with_info: [
|
||||
{
|
||||
version: "1",
|
||||
imports: [
|
||||
"android.hardware.audio.common-V1",
|
||||
"android.media.audio.common.types-V1",
|
||||
],
|
||||
},
|
||||
{
|
||||
version: "2",
|
||||
imports: [
|
||||
"android.hardware.audio.common-V1",
|
||||
"android.media.audio.common.types-V1",
|
||||
],
|
||||
},
|
||||
|
||||
],
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
b9c1e8584d328e39eac9eff2ef039a6541186cc4
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.automotive.audiocontrol;
|
||||
@Backing(type="int") @VintfStability
|
||||
enum AudioFocusChange {
|
||||
NONE = 0,
|
||||
GAIN = 1,
|
||||
GAIN_TRANSIENT = 2,
|
||||
GAIN_TRANSIENT_MAY_DUCK = 3,
|
||||
GAIN_TRANSIENT_EXCLUSIVE = 4,
|
||||
LOSS = -1,
|
||||
LOSS_TRANSIENT = -2,
|
||||
LOSS_TRANSIENT_CAN_DUCK = -3,
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.automotive.audiocontrol;
|
||||
@VintfStability
|
||||
parcelable AudioGainConfigInfo {
|
||||
int zoneId;
|
||||
String devicePortAddress;
|
||||
int volumeIndex;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.automotive.audiocontrol;
|
||||
@VintfStability
|
||||
parcelable DuckingInfo {
|
||||
int zoneId;
|
||||
String[] deviceAddressesToDuck;
|
||||
String[] deviceAddressesToUnduck;
|
||||
String[] usagesHoldingFocus;
|
||||
@nullable android.hardware.audio.common.PlaybackTrackMetadata[] playbackMetaDataHoldingFocus;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.automotive.audiocontrol;
|
||||
@VintfStability
|
||||
interface IAudioControl {
|
||||
/**
|
||||
* @deprecated use {@link android.hardware.audio.common.PlaybackTrackMetadata} instead.
|
||||
*/
|
||||
oneway void onAudioFocusChange(in String usage, in int zoneId, in android.hardware.automotive.audiocontrol.AudioFocusChange focusChange);
|
||||
oneway void onDevicesToDuckChange(in android.hardware.automotive.audiocontrol.DuckingInfo[] duckingInfos);
|
||||
oneway void onDevicesToMuteChange(in android.hardware.automotive.audiocontrol.MutingInfo[] mutingInfos);
|
||||
oneway void registerFocusListener(in android.hardware.automotive.audiocontrol.IFocusListener listener);
|
||||
oneway void setBalanceTowardRight(in float value);
|
||||
oneway void setFadeTowardFront(in float value);
|
||||
oneway void onAudioFocusChangeWithMetaData(in android.hardware.audio.common.PlaybackTrackMetadata playbackMetaData, in int zoneId, in android.hardware.automotive.audiocontrol.AudioFocusChange focusChange);
|
||||
oneway void setAudioDeviceGainsChanged(in android.hardware.automotive.audiocontrol.Reasons[] reasons, in android.hardware.automotive.audiocontrol.AudioGainConfigInfo[] gains);
|
||||
oneway void registerGainCallback(in android.hardware.automotive.audiocontrol.IAudioGainCallback callback);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.automotive.audiocontrol;
|
||||
@VintfStability
|
||||
interface IAudioGainCallback {
|
||||
oneway void onAudioDeviceGainsChanged(in android.hardware.automotive.audiocontrol.Reasons[] reasons, in android.hardware.automotive.audiocontrol.AudioGainConfigInfo[] gains);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.automotive.audiocontrol;
|
||||
@VintfStability
|
||||
interface IFocusListener {
|
||||
oneway void abandonAudioFocus(in String usage, in int zoneId);
|
||||
oneway void requestAudioFocus(in String usage, in int zoneId, in android.hardware.automotive.audiocontrol.AudioFocusChange focusGain);
|
||||
oneway void abandonAudioFocusWithMetaData(in android.hardware.audio.common.PlaybackTrackMetadata playbackMetaData, in int zoneId);
|
||||
oneway void requestAudioFocusWithMetaData(in android.hardware.audio.common.PlaybackTrackMetadata playbackMetaData, in int zoneId, in android.hardware.automotive.audiocontrol.AudioFocusChange focusGain);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.automotive.audiocontrol;
|
||||
@VintfStability
|
||||
parcelable MutingInfo {
|
||||
int zoneId;
|
||||
String[] deviceAddressesToMute;
|
||||
String[] deviceAddressesToUnmute;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.automotive.audiocontrol;
|
||||
@Backing(type="int") @VintfStability
|
||||
enum Reasons {
|
||||
FORCED_MASTER_MUTE = 1,
|
||||
REMOTE_MUTE = 2,
|
||||
TCU_MUTE = 4,
|
||||
ADAS_DUCKING = 8,
|
||||
NAV_DUCKING = 16,
|
||||
PROJECTION_DUCKING = 32,
|
||||
THERMAL_LIMITATION = 64,
|
||||
SUSPEND_EXIT_VOL_LIMITATION = 128,
|
||||
EXTERNAL_AMP_VOL_FEEDBACK = 256,
|
||||
OTHER = -2147483648,
|
||||
}
|
||||
@@ -32,7 +32,7 @@
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.automotive.audiocontrol;
|
||||
@JavaDerive(equals=true, toString=true) @VintfStability
|
||||
@VintfStability
|
||||
parcelable AudioGainConfigInfo {
|
||||
int zoneId;
|
||||
String devicePortAddress;
|
||||
|
||||
@@ -21,7 +21,6 @@ package android.hardware.automotive.audiocontrol;
|
||||
* Was expecting to reuse android.media.audio types... Limit info to minimum to prevent
|
||||
* duplicating aidl_api. Will follow up if AudioGainConfig is exposed by android.media AIDL API.
|
||||
*/
|
||||
@JavaDerive(equals=true, toString=true)
|
||||
@VintfStability
|
||||
parcelable AudioGainConfigInfo {
|
||||
/**
|
||||
|
||||
@@ -18,47 +18,47 @@ package android.hardware.automotive.audiocontrol;
|
||||
|
||||
import android.hardware.audio.common.PlaybackTrackMetadata;
|
||||
|
||||
/**
|
||||
* The current ducking information for a single audio zone.
|
||||
*
|
||||
* <p>This includes devices to duck, as well as unduck based on the contents of a previous
|
||||
* {@link DuckingInfo}. Additionally, the current usages holding focus in the specified zone are
|
||||
* included, which were used to determine which addresses to duck.
|
||||
*/
|
||||
@VintfStability
|
||||
parcelable DuckingInfo {
|
||||
/**
|
||||
* ID of the associated audio zone
|
||||
*/
|
||||
int zoneId;
|
||||
/**
|
||||
* The current ducking information for a single audio zone.
|
||||
*
|
||||
* <p>This includes devices to duck, as well as unduck based on the contents of a previous
|
||||
* {@link DuckingInfo}. Additionally, the current usages holding focus in the specified zone are
|
||||
* included, which were used to determine which addresses to duck.
|
||||
*/
|
||||
@VintfStability
|
||||
parcelable DuckingInfo {
|
||||
/**
|
||||
* ID of the associated audio zone
|
||||
*/
|
||||
int zoneId;
|
||||
|
||||
/**
|
||||
* List of addresses for audio output devices that should be ducked.
|
||||
*
|
||||
* <p>The provided address strings are defined in audio_policy_configuration.xml.
|
||||
*/
|
||||
String[] deviceAddressesToDuck;
|
||||
/**
|
||||
* List of addresses for audio output devices that should be ducked.
|
||||
*
|
||||
* <p>The provided address strings are defined in audio_policy_configuration.xml.
|
||||
*/
|
||||
String[] deviceAddressesToDuck;
|
||||
|
||||
/**
|
||||
* List of addresses for audio output devices that were previously be ducked and should now be
|
||||
* unducked.
|
||||
*
|
||||
* <p>The provided address strings are defined in audio_policy_configuration.xml.
|
||||
*/
|
||||
String[] deviceAddressesToUnduck;
|
||||
/**
|
||||
* List of addresses for audio output devices that were previously be ducked and should now be
|
||||
* unducked.
|
||||
*
|
||||
* <p>The provided address strings are defined in audio_policy_configuration.xml.
|
||||
*/
|
||||
String[] deviceAddressesToUnduck;
|
||||
|
||||
/**
|
||||
* List of usages currently holding focus for this audio zone.
|
||||
*
|
||||
* This field was deprecated in version 2.
|
||||
* Use playbackMetaDataHoldingFocus instead.
|
||||
*
|
||||
* <p> See {@code audioUsage} in audio_policy_configuration.xsd for the list of allowed values.
|
||||
*/
|
||||
String[] usagesHoldingFocus;
|
||||
/**
|
||||
* List of usages currently holding focus for this audio zone.
|
||||
*
|
||||
* This field was deprecated in version 2.
|
||||
* Use playbackMetaDataHoldingFocus instead.
|
||||
*
|
||||
* <p> See {@code audioUsage} in audio_policy_configuration.xsd for the list of allowed values.
|
||||
*/
|
||||
String[] usagesHoldingFocus;
|
||||
|
||||
/**
|
||||
* List of output stream metadata associated with the current focus holder for this audio zone
|
||||
*/
|
||||
@nullable PlaybackTrackMetadata[] playbackMetaDataHoldingFocus;
|
||||
/**
|
||||
* List of output stream metadata associated with the current focus holder for this audio zone
|
||||
*/
|
||||
@nullable PlaybackTrackMetadata[] playbackMetaDataHoldingFocus;
|
||||
}
|
||||
|
||||
@@ -16,14 +16,6 @@
|
||||
|
||||
package android.hardware.automotive.audiocontrol;
|
||||
|
||||
import android.hardware.automotive.audiocontrol.AudioFocusChange;
|
||||
import android.hardware.automotive.audiocontrol.DuckingInfo;
|
||||
import android.hardware.automotive.audiocontrol.MutingInfo;
|
||||
import android.hardware.automotive.audiocontrol.IFocusListener;
|
||||
import android.hardware.automotive.audiocontrol.IAudioGainCallback;
|
||||
import android.hardware.automotive.audiocontrol.AudioGainConfigInfo;
|
||||
import android.hardware.automotive.audiocontrol.Reasons;
|
||||
|
||||
/**
|
||||
* Important note on Metadata:
|
||||
* Metadata qualifies a playback track for an output stream.
|
||||
@@ -54,6 +46,13 @@ import android.hardware.automotive.audiocontrol.Reasons;
|
||||
* audio_policy_engine_configuration.xml file.
|
||||
*/
|
||||
import android.hardware.audio.common.PlaybackTrackMetadata;
|
||||
import android.hardware.automotive.audiocontrol.AudioFocusChange;
|
||||
import android.hardware.automotive.audiocontrol.AudioGainConfigInfo;
|
||||
import android.hardware.automotive.audiocontrol.DuckingInfo;
|
||||
import android.hardware.automotive.audiocontrol.IAudioGainCallback;
|
||||
import android.hardware.automotive.audiocontrol.IFocusListener;
|
||||
import android.hardware.automotive.audiocontrol.MutingInfo;
|
||||
import android.hardware.automotive.audiocontrol.Reasons;
|
||||
|
||||
/**
|
||||
* Interacts with the car's audio subsystem to manage audio sources and volumes
|
||||
@@ -90,19 +89,19 @@ interface IAudioControl {
|
||||
* @param duckingInfos an array of {@link DuckingInfo} objects for the audio zones where audio
|
||||
* focus has changed.
|
||||
*/
|
||||
oneway void onDevicesToDuckChange(in DuckingInfo[] duckingInfos);
|
||||
oneway void onDevicesToDuckChange(in DuckingInfo[] duckingInfos);
|
||||
|
||||
/**
|
||||
* Notifies HAL of changes in output devices that the HAL should apply muting to.
|
||||
*
|
||||
* This will be called in response to changes in audio mute state for each volume group
|
||||
* and will include a {@link MutingInfo} object per audio zone that experienced a mute state
|
||||
* event.
|
||||
*
|
||||
* @param mutingInfos an array of {@link MutingInfo} objects for the audio zones where audio
|
||||
* mute state has changed.
|
||||
*/
|
||||
oneway void onDevicesToMuteChange(in MutingInfo[] mutingInfos);
|
||||
/**
|
||||
* Notifies HAL of changes in output devices that the HAL should apply muting to.
|
||||
*
|
||||
* This will be called in response to changes in audio mute state for each volume group
|
||||
* and will include a {@link MutingInfo} object per audio zone that experienced a mute state
|
||||
* event.
|
||||
*
|
||||
* @param mutingInfos an array of {@link MutingInfo} objects for the audio zones where audio
|
||||
* mute state has changed.
|
||||
*/
|
||||
oneway void onDevicesToMuteChange(in MutingInfo[] mutingInfos);
|
||||
|
||||
/**
|
||||
* Registers focus listener to be used by HAL for requesting and abandoning audio focus.
|
||||
@@ -153,9 +152,9 @@ interface IAudioControl {
|
||||
* @param focusChange the AudioFocusChange that has occurred.
|
||||
*/
|
||||
oneway void onAudioFocusChangeWithMetaData(in PlaybackTrackMetadata playbackMetaData,
|
||||
in int zoneId, in AudioFocusChange focusChange);
|
||||
in int zoneId, in AudioFocusChange focusChange);
|
||||
|
||||
/**
|
||||
/**
|
||||
* Notifies HAL of changes in output devices that the HAL should apply gain change to
|
||||
* and the reason(s) why
|
||||
*
|
||||
@@ -168,8 +167,8 @@ interface IAudioControl {
|
||||
* {@link android.hardware.automotive.audiocontrol.Reasons} constants.
|
||||
*
|
||||
* @param gains List of gains the change is intended to.
|
||||
*/
|
||||
oneway void setAudioDeviceGainsChanged(in Reasons[] reasons, in AudioGainConfigInfo[] gains);
|
||||
*/
|
||||
oneway void setAudioDeviceGainsChanged(in Reasons[] reasons, in AudioGainConfigInfo[] gains);
|
||||
|
||||
/**
|
||||
* Registers callback to be used by HAL for reporting unexpected gain(s) changed and the
|
||||
|
||||
@@ -16,9 +16,8 @@
|
||||
|
||||
package android.hardware.automotive.audiocontrol;
|
||||
|
||||
import android.hardware.automotive.audiocontrol.AudioFocusChange;
|
||||
|
||||
import android.hardware.audio.common.PlaybackTrackMetadata;
|
||||
import android.hardware.automotive.audiocontrol.AudioFocusChange;
|
||||
|
||||
/**
|
||||
* Callback interface for audio focus listener.
|
||||
@@ -72,8 +71,8 @@ interface IFocusListener {
|
||||
* @param playbackMetaData The output stream metadata associated with the focus request
|
||||
* @param zoneId The identifier for the audio zone that the HAL abandoning focus
|
||||
*/
|
||||
oneway void abandonAudioFocusWithMetaData(in PlaybackTrackMetadata playbackMetaData,
|
||||
in int zoneId);
|
||||
oneway void abandonAudioFocusWithMetaData(
|
||||
in PlaybackTrackMetadata playbackMetaData, in int zoneId);
|
||||
|
||||
/**
|
||||
* Used to indicate that the audio output stream associated with
|
||||
@@ -87,5 +86,5 @@ interface IFocusListener {
|
||||
* constants.
|
||||
*/
|
||||
oneway void requestAudioFocusWithMetaData(in PlaybackTrackMetadata playbackMetaData,
|
||||
in int zoneId, in AudioFocusChange focusGain);
|
||||
in int zoneId, in AudioFocusChange focusGain);
|
||||
}
|
||||
|
||||
@@ -28,58 +28,58 @@ enum Reasons {
|
||||
* This may be used for example in case of cyber attach to ensure driver can safely drive back
|
||||
* to garage to restore sw.
|
||||
*/
|
||||
FORCED_MASTER_MUTE = 0x1,
|
||||
FORCED_MASTER_MUTE = 0x1,
|
||||
/**
|
||||
* Reports a mute request outside the IVI (Android) system.
|
||||
* It may target to mute the list of
|
||||
* {@link android.hardware.automotive.audiocontrol.AudioGainConfigInfo}.
|
||||
* A focus request may also be reported in addition if the use case that initiates the mute
|
||||
* has matching {@link android.hardware.automotive.audiocontrol.PlaybackTrackMetadata}
|
||||
* For regulation issue, the action of mute could be managed by HAL itself.
|
||||
* Reports a mute request outside the IVI (Android) system.
|
||||
* It may target to mute the list of
|
||||
* {@link android.hardware.automotive.audiocontrol.AudioGainConfigInfo}.
|
||||
* A focus request may also be reported in addition if the use case that initiates the mute
|
||||
* has matching {@link android.hardware.automotive.audiocontrol.PlaybackTrackMetadata}
|
||||
* For regulation issue, the action of mute could be managed by HAL itself.
|
||||
*/
|
||||
REMOTE_MUTE = 0x2,
|
||||
REMOTE_MUTE = 0x2,
|
||||
/**
|
||||
* Reports a mute initiated by the TCU. It may be applied to all audio source (no
|
||||
* associated {@link android.hardware.automotive.audiocontrol.AudioGainConfigInfo} reported, or
|
||||
* it may target to mute only the given list of ports.
|
||||
* A focus request may also be reported in addition.
|
||||
* For regulation issue, the action of mute could be managed by HAL itself.
|
||||
* Reports a mute initiated by the TCU. It may be applied to all audio source (no
|
||||
* associated {@link android.hardware.automotive.audiocontrol.AudioGainConfigInfo} reported, or
|
||||
* it may target to mute only the given list of ports.
|
||||
* A focus request may also be reported in addition.
|
||||
* For regulation issue, the action of mute could be managed by HAL itself.
|
||||
*/
|
||||
TCU_MUTE = 0x4,
|
||||
TCU_MUTE = 0x4,
|
||||
/**
|
||||
* Reports a duck due to ADAS use case. A focus request may also be reported in addition.
|
||||
* For regulation issue, the action of duck could be managed by HAL itself.
|
||||
* It gives a chance to CarAudioService to decide whether contextual volume change may be
|
||||
* applied from the ducked index base or not.
|
||||
*/
|
||||
ADAS_DUCKING = 0x8,
|
||||
ADAS_DUCKING = 0x8,
|
||||
/**
|
||||
* Reports a duck due to navigation use case. It gives a chance to CarAudioService to decide
|
||||
* whether contextual volume change may be applied from the ducked index base or not.
|
||||
*/
|
||||
NAV_DUCKING = 0x10,
|
||||
NAV_DUCKING = 0x10,
|
||||
/**
|
||||
* Some device projection stack may send signal to IVI to duck / unduck main audio stream.
|
||||
* In this case, Contextual Volume Policy may be adapted to control the alternate / secondary
|
||||
* audio stream.
|
||||
*/
|
||||
PROJECTION_DUCKING = 0x20,
|
||||
PROJECTION_DUCKING = 0x20,
|
||||
/**
|
||||
* When the amplifier is overheating, it may be recovered by limiting the volume.
|
||||
*/
|
||||
THERMAL_LIMITATION = 0x40,
|
||||
THERMAL_LIMITATION = 0x40,
|
||||
/**
|
||||
* Before the system enters suspend, it may ensure while exiting suspend or during cold boot
|
||||
* that the volume is limited to prevent from sound explosion.
|
||||
*/
|
||||
SUSPEND_EXIT_VOL_LIMITATION = 0x80,
|
||||
SUSPEND_EXIT_VOL_LIMITATION = 0x80,
|
||||
/**
|
||||
* When using an external amplifier, it may be required to keep volume in sync and have
|
||||
* asynchronous notification of effective volume change.
|
||||
*/
|
||||
EXTERNAL_AMP_VOL_FEEDBACK = 0x100,
|
||||
EXTERNAL_AMP_VOL_FEEDBACK = 0x100,
|
||||
/**
|
||||
* For other OEM use.
|
||||
*/
|
||||
OTHER = 0x80000000,
|
||||
OTHER = 0x80000000,
|
||||
}
|
||||
|
||||
@@ -29,8 +29,9 @@ cc_binary {
|
||||
vendor: true,
|
||||
shared_libs: [
|
||||
"android.hardware.audio.common@7.0-enums",
|
||||
"android.hardware.audio.common-V1-ndk",
|
||||
"android.frameworks.automotive.powerpolicy-V1-ndk",
|
||||
"android.hardware.automotive.audiocontrol-V1-ndk",
|
||||
"android.hardware.automotive.audiocontrol-V2-ndk",
|
||||
"libbase",
|
||||
"libbinder_ndk",
|
||||
"libcutils",
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include <android_audio_policy_configuration_V7_0-enums.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
namespace aidl::android::hardware::automotive::audiocontrol {
|
||||
@@ -147,6 +149,47 @@ ndk::ScopedAStatus AudioControl::onDevicesToMuteChange(
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
template <typename aidl_type>
|
||||
static inline std::string toString(const std::vector<aidl_type>& in_values) {
|
||||
return std::accumulate(std::begin(in_values), std::end(in_values), std::string{},
|
||||
[](std::string& ls, const aidl_type& rs) {
|
||||
return ls += (ls.empty() ? "" : ",") + rs.toString();
|
||||
});
|
||||
}
|
||||
template <typename aidl_enum_type>
|
||||
static inline std::string toEnumString(const std::vector<aidl_enum_type>& in_values) {
|
||||
return std::accumulate(std::begin(in_values), std::end(in_values), std::string{},
|
||||
[](std::string& ls, const aidl_enum_type& rs) {
|
||||
return ls += (ls.empty() ? "" : ",") + toString(rs);
|
||||
});
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus AudioControl::onAudioFocusChangeWithMetaData(
|
||||
const audiohalcommon::PlaybackTrackMetadata& in_playbackMetaData, int32_t in_zoneId,
|
||||
AudioFocusChange in_focusChange) {
|
||||
LOG(INFO) << "Focus changed: " << toString(in_focusChange).c_str() << " for metadata "
|
||||
<< in_playbackMetaData.toString().c_str() << " in zone " << in_zoneId;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus AudioControl::setAudioDeviceGainsChanged(
|
||||
const std::vector<Reasons>& in_reasons, const std::vector<AudioGainConfigInfo>& in_gains) {
|
||||
LOG(INFO) << "Audio Device Gains changed: resons:" << toEnumString(in_reasons).c_str()
|
||||
<< " for devices: " << toString(in_gains).c_str();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus AudioControl::registerGainCallback(
|
||||
const std::shared_ptr<IAudioGainCallback>& in_callback) {
|
||||
LOG(DEBUG) << ": " << __func__;
|
||||
if (in_callback) {
|
||||
std::atomic_store(&mAudioGainCallback, in_callback);
|
||||
} else {
|
||||
LOG(ERROR) << "Unexpected nullptr for audio gain callback resulting in no-op.";
|
||||
}
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
binder_status_t AudioControl::dump(int fd, const char** args, uint32_t numArgs) {
|
||||
if (numArgs == 0) {
|
||||
return dumpsys(fd);
|
||||
@@ -159,6 +202,12 @@ binder_status_t AudioControl::dump(int fd, const char** args, uint32_t numArgs)
|
||||
return cmdRequestFocus(fd, args, numArgs);
|
||||
} else if (EqualsIgnoreCase(option, "--abandon")) {
|
||||
return cmdAbandonFocus(fd, args, numArgs);
|
||||
} else if (EqualsIgnoreCase(option, "--requestFocusWithMetaData")) {
|
||||
return cmdRequestFocusWithMetaData(fd, args, numArgs);
|
||||
} else if (EqualsIgnoreCase(option, "--abandonFocusWithMetaData")) {
|
||||
return cmdAbandonFocusWithMetaData(fd, args, numArgs);
|
||||
} else if (EqualsIgnoreCase(option, "--audioGainCallback")) {
|
||||
return cmdOnAudioDeviceGainsChanged(fd, args, numArgs);
|
||||
} else {
|
||||
dprintf(fd, "Invalid option: %s\n", option.c_str());
|
||||
return STATUS_BAD_VALUE;
|
||||
@@ -171,20 +220,49 @@ binder_status_t AudioControl::dumpsys(int fd) {
|
||||
} else {
|
||||
dprintf(fd, "Focus listener registered\n");
|
||||
}
|
||||
dprintf(fd, "AudioGainCallback %sregistered\n", (mAudioGainCallback == nullptr ? "NOT " : ""));
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
binder_status_t AudioControl::cmdHelp(int fd) const {
|
||||
dprintf(fd, "Usage: \n\n");
|
||||
dprintf(fd, "[no args]: dumps focus listener status\n");
|
||||
dprintf(fd, "[no args]: dumps focus listener / gain callback registered status\n");
|
||||
dprintf(fd, "--help: shows this help\n");
|
||||
dprintf(fd,
|
||||
"--request <USAGE> <ZONE_ID> <FOCUS_GAIN>: requests audio focus for specified "
|
||||
"usage (string), audio zone ID (int), and focus gain type (int)\n");
|
||||
"usage (string), audio zone ID (int), and focus gain type (int)\n"
|
||||
"Deprecated, use MetaData instead\n");
|
||||
dprintf(fd,
|
||||
"--abandon <USAGE> <ZONE_ID>: abandons audio focus for specified usage (string) and "
|
||||
"audio zone ID (int)\n");
|
||||
"audio zone ID (int)\n"
|
||||
"Deprecated, use MetaData instead\n");
|
||||
dprintf(fd, "See audio_policy_configuration.xsd for valid AudioUsage values.\n");
|
||||
|
||||
dprintf(fd,
|
||||
"--requestFocusWithMetaData <METADATA> <ZONE_ID> <FOCUS_GAIN>: "
|
||||
"requests audio focus for specified metadata, audio zone ID (int), "
|
||||
"and focus gain type (int)\n");
|
||||
dprintf(fd,
|
||||
"--abandonFocusWithMetaData <METADATA> <ZONE_ID>: "
|
||||
"abandons audio focus for specified metadata and audio zone ID (int)\n");
|
||||
dprintf(fd,
|
||||
"--audioGainCallback <ZONE_ID> <REASON_1>[,<REASON_N> ...]"
|
||||
"<DEVICE_ADDRESS_1> <GAIN_INDEX_1> [<DEVICE_ADDRESS_N> <GAIN_INDEX_N> ...]: fire audio "
|
||||
"gain callback for audio zone ID (int), the given reasons (csv int) for given pairs "
|
||||
"of device address (string) and gain index (int) \n");
|
||||
|
||||
dprintf(fd,
|
||||
"Note on <METADATA>: <USAGE,CONTENT_TYPE[,TAGS]> specified as where (int)usage, "
|
||||
"(int)content type and tags (string)string)\n");
|
||||
dprintf(fd,
|
||||
"See android/media/audio/common/AudioUsageType.aidl for valid AudioUsage values.\n");
|
||||
dprintf(fd,
|
||||
"See android/media/audio/common/AudioContentType.aidl for valid AudioContentType "
|
||||
"values.\n");
|
||||
dprintf(fd,
|
||||
"Tags are optional. If provided, it must follow the <key>=<value> pattern, where the "
|
||||
"value is namespaced (for example com.google.strategy=VR).\n");
|
||||
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -266,4 +344,174 @@ binder_status_t AudioControl::cmdAbandonFocus(int fd, const char** args, uint32_
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
binder_status_t AudioControl::parseMetaData(int fd, const std::string& metadataLiteral,
|
||||
audiohalcommon::PlaybackTrackMetadata& trackMetadata) {
|
||||
std::stringstream csvMetaData(metadataLiteral);
|
||||
std::vector<std::string> splitMetaData;
|
||||
std::string attribute;
|
||||
while (getline(csvMetaData, attribute, ',')) {
|
||||
splitMetaData.push_back(attribute);
|
||||
}
|
||||
if (splitMetaData.size() != 2 && splitMetaData.size() != 3) {
|
||||
dprintf(fd,
|
||||
"Invalid metadata: %s, please provide <METADATA> as <USAGE,CONTENT_TYPE[,TAGS]> "
|
||||
"where (int)usage, (int)content type and tags (string)string)\n",
|
||||
metadataLiteral.c_str());
|
||||
return STATUS_BAD_VALUE;
|
||||
}
|
||||
int usage;
|
||||
if (!safelyParseInt(splitMetaData[0], &usage)) {
|
||||
dprintf(fd, "Non-integer usage provided with request: %s\n", splitMetaData[0].c_str());
|
||||
return STATUS_BAD_VALUE;
|
||||
}
|
||||
int contentType;
|
||||
if (!safelyParseInt(splitMetaData[1], &contentType)) {
|
||||
dprintf(fd, "Non-integer content type provided with request: %s\n",
|
||||
splitMetaData[1].c_str());
|
||||
return STATUS_BAD_VALUE;
|
||||
}
|
||||
const std::string tags = (splitMetaData.size() == 3 ? splitMetaData[2] : "");
|
||||
|
||||
trackMetadata = {.usage = static_cast<audiomediacommon::AudioUsage>(usage),
|
||||
.contentType = static_cast<audiomediacommon::AudioContentType>(contentType),
|
||||
.tags = {tags}};
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
binder_status_t AudioControl::cmdRequestFocusWithMetaData(int fd, const char** args,
|
||||
uint32_t numArgs) {
|
||||
if (!checkCallerHasWritePermissions(fd)) {
|
||||
return STATUS_PERMISSION_DENIED;
|
||||
}
|
||||
if (numArgs != 4) {
|
||||
dprintf(fd,
|
||||
"Invalid number of arguments: please provide:\n"
|
||||
"--requestFocusWithMetaData <METADATA> <ZONE_ID> <FOCUS_GAIN>: "
|
||||
"requests audio focus for specified metadata, audio zone ID (int), "
|
||||
"and focus gain type (int)\n");
|
||||
return STATUS_BAD_VALUE;
|
||||
}
|
||||
std::string metadataLiteral = std::string(args[1]);
|
||||
audiohalcommon::PlaybackTrackMetadata trackMetadata{};
|
||||
auto status = parseMetaData(fd, metadataLiteral, trackMetadata);
|
||||
if (status != STATUS_OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
int zoneId;
|
||||
if (!safelyParseInt(std::string(args[2]), &zoneId)) {
|
||||
dprintf(fd, "Non-integer zoneId provided with request: %s\n", std::string(args[2]).c_str());
|
||||
return STATUS_BAD_VALUE;
|
||||
}
|
||||
|
||||
int focusGainValue;
|
||||
if (!safelyParseInt(std::string(args[3]), &focusGainValue)) {
|
||||
dprintf(fd, "Non-integer focusGain provided with request: %s\n",
|
||||
std::string(args[3]).c_str());
|
||||
return STATUS_BAD_VALUE;
|
||||
}
|
||||
AudioFocusChange focusGain = AudioFocusChange(focusGainValue);
|
||||
|
||||
if (mFocusListener == nullptr) {
|
||||
dprintf(fd, "Unable to request focus - no focus listener registered\n");
|
||||
return STATUS_BAD_VALUE;
|
||||
}
|
||||
mFocusListener->requestAudioFocusWithMetaData(trackMetadata, zoneId, focusGain);
|
||||
dprintf(fd, "Requested focus for metadata %s, zoneId %d, and focusGain %d\n",
|
||||
trackMetadata.toString().c_str(), zoneId, focusGain);
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
binder_status_t AudioControl::cmdAbandonFocusWithMetaData(int fd, const char** args,
|
||||
uint32_t numArgs) {
|
||||
if (!checkCallerHasWritePermissions(fd)) {
|
||||
return STATUS_PERMISSION_DENIED;
|
||||
}
|
||||
if (numArgs != 3) {
|
||||
dprintf(fd,
|
||||
"Invalid number of arguments: please provide:\n"
|
||||
"--abandonFocusWithMetaData <METADATA> <ZONE_ID>: "
|
||||
"abandons audio focus for specified metadata and audio zone ID (int)\n");
|
||||
return STATUS_BAD_VALUE;
|
||||
}
|
||||
std::string metadataLiteral = std::string(args[1]);
|
||||
audiohalcommon::PlaybackTrackMetadata trackMetadata{};
|
||||
auto status = parseMetaData(fd, metadataLiteral, trackMetadata);
|
||||
if (status != STATUS_OK) {
|
||||
return status;
|
||||
}
|
||||
int zoneId;
|
||||
if (!safelyParseInt(std::string(args[2]), &zoneId)) {
|
||||
dprintf(fd, "Non-integer zoneId provided with request: %s\n", std::string(args[2]).c_str());
|
||||
return STATUS_BAD_VALUE;
|
||||
}
|
||||
if (mFocusListener == nullptr) {
|
||||
dprintf(fd, "Unable to abandon focus - no focus listener registered\n");
|
||||
return STATUS_BAD_VALUE;
|
||||
}
|
||||
|
||||
mFocusListener->abandonAudioFocusWithMetaData(trackMetadata, zoneId);
|
||||
dprintf(fd, "Abandoned focus for metadata %s and zoneId %d\n", trackMetadata.toString().c_str(),
|
||||
zoneId);
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
binder_status_t AudioControl::cmdOnAudioDeviceGainsChanged(int fd, const char** args,
|
||||
uint32_t numArgs) {
|
||||
if (!checkCallerHasWritePermissions(fd)) {
|
||||
return STATUS_PERMISSION_DENIED;
|
||||
}
|
||||
if ((numArgs + 1) % 2 != 0) {
|
||||
dprintf(fd,
|
||||
"Invalid number of arguments: please provide\n"
|
||||
"--audioGainCallback <ZONE_ID> <REASON_1>[,<REASON_N> ...]"
|
||||
"<DEVICE_ADDRESS_1> <GAIN_INDEX_1> [<DEVICE_ADDRESS_N> <GAIN_INDEX_N> ...]: "
|
||||
"fire audio gain callback for audio zone ID (int), "
|
||||
"with the given reasons (csv int) for given pairs of device address (string) "
|
||||
"and gain index (int) \n");
|
||||
return STATUS_BAD_VALUE;
|
||||
}
|
||||
int zoneId;
|
||||
if (!safelyParseInt(string(args[1]), &zoneId)) {
|
||||
dprintf(fd, "Non-integer zoneId provided with request: %s\n", std::string(args[1]).c_str());
|
||||
return STATUS_BAD_VALUE;
|
||||
}
|
||||
std::string reasonsLiteral = std::string(args[2]);
|
||||
std::stringstream csvReasonsLiteral(reasonsLiteral);
|
||||
std::vector<Reasons> reasons;
|
||||
std::string reasonLiteral;
|
||||
while (getline(csvReasonsLiteral, reasonLiteral, ',')) {
|
||||
int reason;
|
||||
if (!safelyParseInt(reasonLiteral, &reason)) {
|
||||
dprintf(fd, "Invalid Reason(s) provided %s\n", reasonLiteral.c_str());
|
||||
return STATUS_BAD_VALUE;
|
||||
}
|
||||
reasons.push_back(static_cast<Reasons>(reason));
|
||||
}
|
||||
|
||||
std::vector<AudioGainConfigInfo> agcis{};
|
||||
for (uint32_t i = 3; i < numArgs; i += 2) {
|
||||
std::string deviceAddress = std::string(args[i]);
|
||||
int32_t index;
|
||||
if (!safelyParseInt(std::string(args[i + 1]), &index)) {
|
||||
dprintf(fd, "Non-integer index provided with request: %s\n",
|
||||
std::string(args[i + 1]).c_str());
|
||||
return STATUS_BAD_VALUE;
|
||||
}
|
||||
AudioGainConfigInfo agci{zoneId, deviceAddress, index};
|
||||
agcis.push_back(agci);
|
||||
}
|
||||
if (mAudioGainCallback == nullptr) {
|
||||
dprintf(fd,
|
||||
"Unable to trig audio gain callback for reasons=%s and gains=%s\n"
|
||||
" - no audio gain callback registered\n",
|
||||
toEnumString(reasons).c_str(), toString(agcis).c_str());
|
||||
return STATUS_BAD_VALUE;
|
||||
}
|
||||
|
||||
mAudioGainCallback->onAudioDeviceGainsChanged(reasons, agcis);
|
||||
dprintf(fd, "Fired audio gain callback for reasons=%s and gains=%s\n",
|
||||
toEnumString(reasons).c_str(), toString(agcis).c_str());
|
||||
return STATUS_OK;
|
||||
}
|
||||
} // namespace aidl::android::hardware::automotive::audiocontrol
|
||||
|
||||
@@ -17,12 +17,20 @@
|
||||
#define ANDROID_HARDWARE_AUTOMOTIVE_AUDIOCONTROL_AUDIOCONTROL_H
|
||||
|
||||
#include <aidl/android/hardware/automotive/audiocontrol/AudioFocusChange.h>
|
||||
#include <aidl/android/hardware/automotive/audiocontrol/AudioGainConfigInfo.h>
|
||||
#include <aidl/android/hardware/automotive/audiocontrol/BnAudioControl.h>
|
||||
#include <aidl/android/hardware/automotive/audiocontrol/DuckingInfo.h>
|
||||
#include <aidl/android/hardware/automotive/audiocontrol/IAudioGainCallback.h>
|
||||
#include <aidl/android/hardware/automotive/audiocontrol/MutingInfo.h>
|
||||
#include <aidl/android/hardware/automotive/audiocontrol/Reasons.h>
|
||||
|
||||
#include <aidl/android/hardware/audio/common/PlaybackTrackMetadata.h>
|
||||
|
||||
namespace aidl::android::hardware::automotive::audiocontrol {
|
||||
|
||||
namespace audiohalcommon = ::aidl::android::hardware::audio::common;
|
||||
namespace audiomediacommon = ::aidl::android::media::audio::common;
|
||||
|
||||
class AudioControl : public BnAudioControl {
|
||||
public:
|
||||
ndk::ScopedAStatus onAudioFocusChange(const std::string& in_usage, int32_t in_zoneId,
|
||||
@@ -35,6 +43,15 @@ class AudioControl : public BnAudioControl {
|
||||
const std::shared_ptr<IFocusListener>& in_listener) override;
|
||||
ndk::ScopedAStatus setBalanceTowardRight(float in_value) override;
|
||||
ndk::ScopedAStatus setFadeTowardFront(float in_value) override;
|
||||
ndk::ScopedAStatus onAudioFocusChangeWithMetaData(
|
||||
const audiohalcommon::PlaybackTrackMetadata& in_playbackMetaData, int32_t in_zoneId,
|
||||
AudioFocusChange in_focusChange) override;
|
||||
ndk::ScopedAStatus setAudioDeviceGainsChanged(
|
||||
const std::vector<Reasons>& in_reasons,
|
||||
const std::vector<AudioGainConfigInfo>& in_gains) override;
|
||||
ndk::ScopedAStatus registerGainCallback(
|
||||
const std::shared_ptr<IAudioGainCallback>& in_callback) override;
|
||||
|
||||
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
|
||||
|
||||
private:
|
||||
@@ -44,9 +61,22 @@ class AudioControl : public BnAudioControl {
|
||||
// listener, then it should also include mutexes or make the listener atomic.
|
||||
std::shared_ptr<IFocusListener> mFocusListener;
|
||||
|
||||
/**
|
||||
* @brief mAudioGainCallback will be used by this HAL instance to communicate e.g. with a single
|
||||
* instance of CarAudioService to report unexpected gain changed.
|
||||
*/
|
||||
std::shared_ptr<IAudioGainCallback> mAudioGainCallback = nullptr;
|
||||
|
||||
binder_status_t cmdHelp(int fd) const;
|
||||
binder_status_t cmdRequestFocus(int fd, const char** args, uint32_t numArgs);
|
||||
binder_status_t cmdAbandonFocus(int fd, const char** args, uint32_t numArgs);
|
||||
binder_status_t cmdRequestFocusWithMetaData(int fd, const char** args, uint32_t numArgs);
|
||||
binder_status_t cmdAbandonFocusWithMetaData(int fd, const char** args, uint32_t numArgs);
|
||||
binder_status_t cmdOnAudioDeviceGainsChanged(int fd, const char** args, uint32_t numArgs);
|
||||
|
||||
binder_status_t parseMetaData(int fd, const std::string& metadataLiteral,
|
||||
audiohalcommon::PlaybackTrackMetadata& trackMetadata);
|
||||
|
||||
binder_status_t dumpsys(int fd);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<manifest version="1.0" type="device">
|
||||
<manifest version="2.0" type="device">
|
||||
<hal format="aidl">
|
||||
<name>android.hardware.automotive.audiocontrol</name>
|
||||
<fqname>IAudioControl/default</fqname>
|
||||
|
||||
@@ -37,11 +37,16 @@ cc_test {
|
||||
"libxml2",
|
||||
],
|
||||
static_libs: [
|
||||
"android.hardware.automotive.audiocontrol-V1-cpp",
|
||||
"android.hardware.automotive.audiocontrol-V2-cpp",
|
||||
"android.hardware.audio.common-V1-cpp",
|
||||
"android.media.audio.common.types-V1-cpp",
|
||||
"libgmock",
|
||||
],
|
||||
test_suites: [
|
||||
"general-tests",
|
||||
"vts",
|
||||
],
|
||||
cflags: [
|
||||
"-Wno-deprecated-declarations",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <aidl/Vintf.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include <android/hardware/automotive/audiocontrol/BnAudioGainCallback.h>
|
||||
#include <android/hardware/automotive/audiocontrol/BnFocusListener.h>
|
||||
#include <android/hardware/automotive/audiocontrol/IAudioControl.h>
|
||||
#include <android/log.h>
|
||||
@@ -30,10 +31,13 @@ using android::sp;
|
||||
using android::String16;
|
||||
using android::binder::Status;
|
||||
using android::hardware::automotive::audiocontrol::AudioFocusChange;
|
||||
using android::hardware::automotive::audiocontrol::AudioGainConfigInfo;
|
||||
using android::hardware::automotive::audiocontrol::BnAudioGainCallback;
|
||||
using android::hardware::automotive::audiocontrol::BnFocusListener;
|
||||
using android::hardware::automotive::audiocontrol::DuckingInfo;
|
||||
using android::hardware::automotive::audiocontrol::IAudioControl;
|
||||
using android::hardware::automotive::audiocontrol::MutingInfo;
|
||||
using android::hardware::automotive::audiocontrol::Reasons;
|
||||
|
||||
#include "android_audio_policy_configuration_V7_0.h"
|
||||
|
||||
@@ -41,6 +45,9 @@ namespace xsd {
|
||||
using namespace android::audio::policy::configuration::V7_0;
|
||||
}
|
||||
|
||||
namespace audiohalcommon = android::hardware::audio::common;
|
||||
namespace audiomediacommon = android::media::audio::common;
|
||||
|
||||
class AudioControlAidl : public testing::TestWithParam<std::string> {
|
||||
public:
|
||||
virtual void SetUp() override {
|
||||
@@ -103,6 +110,11 @@ struct FocusListenerMock : BnFocusListener {
|
||||
MOCK_METHOD(Status, requestAudioFocus,
|
||||
(const String16& usage, int32_t zoneId, AudioFocusChange focusGain));
|
||||
MOCK_METHOD(Status, abandonAudioFocus, (const String16& usage, int32_t zoneId));
|
||||
MOCK_METHOD(Status, requestAudioFocusWithMetaData,
|
||||
(const audiohalcommon::PlaybackTrackMetadata& metaData, int32_t zoneId,
|
||||
AudioFocusChange focusGain));
|
||||
MOCK_METHOD(Status, abandonAudioFocusWithMetaData,
|
||||
(const audiohalcommon::PlaybackTrackMetadata& metaData, int32_t zoneId));
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -159,6 +171,61 @@ TEST_P(AudioControlAidl, DuckChangeExercise) {
|
||||
ASSERT_TRUE(audioControl->onDevicesToDuckChange(duckingInfos).isOk());
|
||||
}
|
||||
|
||||
TEST_P(AudioControlAidl, FocusChangeWithMetaDataExercise) {
|
||||
ALOGI("Focus Change test");
|
||||
|
||||
audiohalcommon::PlaybackTrackMetadata metadata;
|
||||
metadata.usage = audiomediacommon::AudioUsage::MEDIA;
|
||||
metadata.contentType = audiomediacommon::AudioContentType::MUSIC;
|
||||
metadata.tags = {"com.google.android=VR"};
|
||||
ASSERT_TRUE(
|
||||
audioControl
|
||||
->onAudioFocusChangeWithMetaData(metadata, 0, AudioFocusChange::GAIN_TRANSIENT)
|
||||
.isOk());
|
||||
};
|
||||
|
||||
TEST_P(AudioControlAidl, SetAudioDeviceGainsChangedExercise) {
|
||||
ALOGI("Set Audio Gains Changed test");
|
||||
|
||||
const std::vector<Reasons> reasons{Reasons::FORCED_MASTER_MUTE, Reasons::NAV_DUCKING};
|
||||
AudioGainConfigInfo agci1;
|
||||
agci1.zoneId = 0;
|
||||
agci1.devicePortAddress = String16("address 1");
|
||||
agci1.volumeIndex = 8;
|
||||
|
||||
AudioGainConfigInfo agci2;
|
||||
agci1.zoneId = 0;
|
||||
agci1.devicePortAddress = String16("address 2");
|
||||
agci1.volumeIndex = 1;
|
||||
|
||||
std::vector<AudioGainConfigInfo> gains{agci1, agci2};
|
||||
ASSERT_TRUE(audioControl->setAudioDeviceGainsChanged(reasons, gains).isOk());
|
||||
}
|
||||
|
||||
/*
|
||||
* Test Audio Gain Callback registration.
|
||||
*
|
||||
* Verifies that:
|
||||
* - registerGainCallback succeeds;
|
||||
* - registering a second callback succeeds in replacing the first;
|
||||
* - closing handle does not crash;
|
||||
*/
|
||||
struct AudioGainCallbackMock : BnAudioGainCallback {
|
||||
MOCK_METHOD(Status, onAudioDeviceGainsChanged,
|
||||
(const std::vector<Reasons>& reasons,
|
||||
const std::vector<AudioGainConfigInfo>& gains));
|
||||
};
|
||||
|
||||
TEST_P(AudioControlAidl, AudioGainCallbackRegistration) {
|
||||
ALOGI("Focus listener test");
|
||||
|
||||
sp<AudioGainCallbackMock> gainCallback = new AudioGainCallbackMock();
|
||||
ASSERT_TRUE(audioControl->registerGainCallback(gainCallback).isOk());
|
||||
|
||||
sp<AudioGainCallbackMock> gainCallback2 = new AudioGainCallbackMock();
|
||||
ASSERT_TRUE(audioControl->registerGainCallback(gainCallback2).isOk());
|
||||
}
|
||||
|
||||
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioControlAidl);
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
Audiocontrol, AudioControlAidl,
|
||||
|
||||
@@ -292,14 +292,6 @@ Return<ICanController::Result> CanController::upInterface(const ICanController::
|
||||
return ICanController::Result::OK;
|
||||
}
|
||||
|
||||
static bool unregisterCanBusService(const hidl_string& name, sp<CanBus> busService) {
|
||||
auto manager = hidl::manager::V1_2::IServiceManager::getService();
|
||||
if (!manager) return false;
|
||||
const auto res = manager->tryUnregister(ICanBus::descriptor, name, busService);
|
||||
if (!res.isOk()) return false;
|
||||
return res;
|
||||
}
|
||||
|
||||
Return<bool> CanController::downInterface(const hidl_string& name) {
|
||||
LOG(VERBOSE) << "Attempting to bring interface down: " << name;
|
||||
|
||||
@@ -313,12 +305,6 @@ Return<bool> CanController::downInterface(const hidl_string& name) {
|
||||
|
||||
auto success = true;
|
||||
|
||||
if (!unregisterCanBusService(name, busEntry.mapped())) {
|
||||
LOG(ERROR) << "Couldn't unregister " << name;
|
||||
// don't return yet, let's try to do best-effort cleanup
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (!busEntry.mapped()->down()) {
|
||||
LOG(ERROR) << "Couldn't bring " << name << " down";
|
||||
success = false;
|
||||
|
||||
@@ -35,6 +35,7 @@ cc_library_static {
|
||||
"protocols/generic/Generic.cpp",
|
||||
"protocols/generic/GenericMessageBase.cpp",
|
||||
"protocols/generic/Unknown.cpp",
|
||||
"protocols/generic/families/Mac80211hwsim.cpp",
|
||||
"protocols/generic/families/Nl80211.cpp",
|
||||
"protocols/route/Link.cpp",
|
||||
"protocols/route/Route.cpp",
|
||||
@@ -42,6 +43,7 @@ cc_library_static {
|
||||
"protocols/MessageDefinition.cpp",
|
||||
"protocols/NetlinkProtocol.cpp",
|
||||
"protocols/all.cpp",
|
||||
"protocols/structs.cpp",
|
||||
"Attributes.cpp",
|
||||
"MessageFactory.cpp",
|
||||
"MessageMutator.cpp",
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
namespace android::nl {
|
||||
|
||||
MessageMutator::MessageMutator(nlmsghdr* buffer, size_t totalLen)
|
||||
: mConstBuffer(buffer, totalLen), mMutableBuffer(buffer) {
|
||||
: mMutableBuffer(buffer), mTotalLen(totalLen) {
|
||||
CHECK(totalLen >= sizeof(nlmsghdr));
|
||||
}
|
||||
|
||||
@@ -27,8 +27,12 @@ nlmsghdr* MessageMutator::operator->() const {
|
||||
return mMutableBuffer;
|
||||
}
|
||||
|
||||
Buffer<nlmsghdr> MessageMutator::constBuffer() const {
|
||||
return {mMutableBuffer, mTotalLen};
|
||||
}
|
||||
|
||||
MessageMutator::operator Buffer<nlmsghdr>() const {
|
||||
return mConstBuffer;
|
||||
return constBuffer();
|
||||
}
|
||||
|
||||
uint64_t MessageMutator::read(Buffer<nlattr> attr) const {
|
||||
@@ -37,7 +41,8 @@ uint64_t MessageMutator::read(Buffer<nlattr> attr) const {
|
||||
|
||||
void MessageMutator::write(Buffer<nlattr> attr, uint64_t val) const {
|
||||
const auto attrData = attr.data<uint64_t>();
|
||||
const auto offset = mConstBuffer.getOffset(attrData);
|
||||
// TODO(b/177251183): deduplicate this code against fragment()
|
||||
const auto offset = constBuffer().getOffset(attrData);
|
||||
CHECK(offset.has_value()) << "Trying to write attribute that's not a member of this message";
|
||||
|
||||
const auto writeableBuffer = reinterpret_cast<uint8_t*>(mMutableBuffer) + *offset;
|
||||
@@ -47,4 +52,40 @@ void MessageMutator::write(Buffer<nlattr> attr, uint64_t val) const {
|
||||
memcpy(writeableBuffer, &val, std::min(sizeof(val), attrSize));
|
||||
}
|
||||
|
||||
MessageMutator MessageMutator::fragment(Buffer<nlmsghdr> buf) const {
|
||||
const auto offset = constBuffer().getOffset(buf);
|
||||
CHECK(offset.has_value()) << "Trying to modify a fragment outside of buffer range";
|
||||
|
||||
const auto writeableBuffer = reinterpret_cast<nlmsghdr*>(uintptr_t(mMutableBuffer) + *offset);
|
||||
const auto len = buf.getRaw().len();
|
||||
CHECK(len <= mTotalLen - *offset);
|
||||
|
||||
return {writeableBuffer, len};
|
||||
}
|
||||
|
||||
MessageMutator::iterator MessageMutator::begin() const {
|
||||
return {*this, constBuffer().begin()};
|
||||
}
|
||||
|
||||
MessageMutator::iterator MessageMutator::end() const {
|
||||
return {*this, constBuffer().end()};
|
||||
}
|
||||
|
||||
MessageMutator::iterator::iterator(const MessageMutator& container,
|
||||
Buffer<nlmsghdr>::iterator current)
|
||||
: mContainer(container), mCurrent(current) {}
|
||||
|
||||
MessageMutator::iterator MessageMutator::iterator::operator++() {
|
||||
++mCurrent;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool MessageMutator::iterator::operator==(const iterator& other) const {
|
||||
return other.mCurrent == mCurrent;
|
||||
}
|
||||
|
||||
const MessageMutator MessageMutator::iterator::operator*() const {
|
||||
return mContainer.fragment(*mCurrent);
|
||||
}
|
||||
|
||||
} // namespace android::nl
|
||||
|
||||
@@ -68,6 +68,11 @@ bool Socket::send(const Buffer<nlmsghdr>& msg, const sockaddr_nl& sa) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Socket::send(const Buffer<nlmsghdr>& msg, uint32_t destination) {
|
||||
sockaddr_nl sa = {.nl_family = AF_NETLINK, .nl_pad = 0, .nl_pid = destination, .nl_groups = 0};
|
||||
return send(msg, sa);
|
||||
}
|
||||
|
||||
bool Socket::increaseReceiveBuffer(size_t maxSize) {
|
||||
if (maxSize == 0) {
|
||||
LOG(ERROR) << "Maximum receive size should not be zero";
|
||||
@@ -157,6 +162,26 @@ pollfd Socket::preparePoll(short events) {
|
||||
return {mFd.get(), events, 0};
|
||||
}
|
||||
|
||||
bool Socket::addMembership(unsigned group) {
|
||||
const auto res =
|
||||
setsockopt(mFd.get(), SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group));
|
||||
if (res < 0) {
|
||||
PLOG(ERROR) << "Failed joining multicast group " << group;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Socket::dropMembership(unsigned group) {
|
||||
const auto res =
|
||||
setsockopt(mFd.get(), SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, &group, sizeof(group));
|
||||
if (res < 0) {
|
||||
PLOG(ERROR) << "Failed leaving multicast group " << group;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Socket::receive_iterator::receive_iterator(Socket& socket, bool end)
|
||||
: mSocket(socket), mIsEnd(end) {
|
||||
if (!end) receive();
|
||||
|
||||
@@ -138,7 +138,7 @@ class Buffer {
|
||||
class raw_iterator : public iterator {
|
||||
public:
|
||||
iterator operator++() {
|
||||
this->mCurrent.mData++; // ignore alignment
|
||||
++this->mCurrent.mData; // ignore alignment
|
||||
return *this;
|
||||
}
|
||||
const T& operator*() const { return *this->mCurrent.mData; }
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace android::nl {
|
||||
* a single instance can only be used by a single thread - the one owning the underlying buffer).
|
||||
*/
|
||||
template <typename T>
|
||||
class Message {
|
||||
class Message : public Buffer<nlmsghdr> {
|
||||
public:
|
||||
/**
|
||||
* Validate buffer contents as a message carrying T data and create instance of parsed message.
|
||||
@@ -51,7 +51,7 @@ class Message {
|
||||
|
||||
const auto attributes = buf.data<nlattr>(sizeof(T));
|
||||
|
||||
return Message<T>(nlHeader, dataHeader, attributes);
|
||||
return Message<T>(buf, nlHeader, dataHeader, attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,8 +94,9 @@ class Message {
|
||||
const T* operator->() const { return &data; }
|
||||
|
||||
private:
|
||||
Message(const nlmsghdr& nlHeader, const T& dataHeader, Attributes attributes)
|
||||
: header(nlHeader), data(dataHeader), attributes(attributes) {}
|
||||
Message(Buffer<nlmsghdr> buffer, const nlmsghdr& nlHeader, const T& dataHeader,
|
||||
Attributes attributes)
|
||||
: Buffer<nlmsghdr>(buffer), header(nlHeader), data(dataHeader), attributes(attributes) {}
|
||||
};
|
||||
|
||||
} // namespace android::nl
|
||||
|
||||
@@ -53,9 +53,27 @@ class MessageMutator {
|
||||
*/
|
||||
void write(Buffer<nlattr> attr, uint64_t val) const;
|
||||
|
||||
class iterator {
|
||||
public:
|
||||
iterator(const MessageMutator& container, Buffer<nlmsghdr>::iterator current);
|
||||
|
||||
iterator operator++();
|
||||
bool operator==(const iterator& other) const;
|
||||
const MessageMutator operator*() const;
|
||||
|
||||
protected:
|
||||
const MessageMutator& mContainer;
|
||||
Buffer<nlmsghdr>::iterator mCurrent;
|
||||
};
|
||||
iterator begin() const;
|
||||
iterator end() const;
|
||||
|
||||
private:
|
||||
const Buffer<nlmsghdr> mConstBuffer;
|
||||
nlmsghdr* mMutableBuffer;
|
||||
size_t mTotalLen;
|
||||
|
||||
Buffer<nlmsghdr> constBuffer() const;
|
||||
MessageMutator fragment(Buffer<nlmsghdr> buf) const;
|
||||
};
|
||||
|
||||
} // namespace android::nl
|
||||
|
||||
@@ -94,6 +94,15 @@ class Socket {
|
||||
*/
|
||||
bool send(const Buffer<nlmsghdr>& msg, const sockaddr_nl& sa);
|
||||
|
||||
/**
|
||||
* Send Netlink message.
|
||||
*
|
||||
* \param msg Message to send.
|
||||
* \param destination Destination PID.
|
||||
* \return true, if succeeded.
|
||||
*/
|
||||
bool send(const Buffer<nlmsghdr>& msg, uint32_t destination);
|
||||
|
||||
/**
|
||||
* Receive one or multiple Netlink messages.
|
||||
*
|
||||
@@ -182,6 +191,22 @@ class Socket {
|
||||
*/
|
||||
pollfd preparePoll(short events = 0);
|
||||
|
||||
/**
|
||||
* Join a multicast group.
|
||||
*
|
||||
* \param group Group ID (*not* a bitfield)
|
||||
* \return whether the operation succeeded
|
||||
*/
|
||||
bool addMembership(unsigned group);
|
||||
|
||||
/**
|
||||
* Leave a multicast group.
|
||||
*
|
||||
* \param group Group ID (*not* a bitfield)
|
||||
* \return whether the operation succeeded
|
||||
*/
|
||||
bool dropMembership(unsigned group);
|
||||
|
||||
/**
|
||||
* Live iterator continuously receiving messages from Netlink socket.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// API definitions from kernel drivers/net/wireless/mac80211_hwsim.h
|
||||
|
||||
#define BIT(n) (1 << (n))
|
||||
|
||||
enum hwsim_tx_control_flags {
|
||||
HWSIM_TX_CTL_REQ_TX_STATUS = BIT(0),
|
||||
HWSIM_TX_CTL_NO_ACK = BIT(1),
|
||||
HWSIM_TX_STAT_ACK = BIT(2),
|
||||
};
|
||||
|
||||
enum {
|
||||
HWSIM_CMD_UNSPEC,
|
||||
HWSIM_CMD_REGISTER,
|
||||
HWSIM_CMD_FRAME,
|
||||
HWSIM_CMD_TX_INFO_FRAME,
|
||||
HWSIM_CMD_NEW_RADIO,
|
||||
HWSIM_CMD_DEL_RADIO,
|
||||
HWSIM_CMD_GET_RADIO,
|
||||
HWSIM_CMD_ADD_MAC_ADDR,
|
||||
HWSIM_CMD_DEL_MAC_ADDR,
|
||||
};
|
||||
|
||||
enum {
|
||||
HWSIM_ATTR_UNSPEC,
|
||||
HWSIM_ATTR_ADDR_RECEIVER,
|
||||
HWSIM_ATTR_ADDR_TRANSMITTER,
|
||||
HWSIM_ATTR_FRAME,
|
||||
HWSIM_ATTR_FLAGS,
|
||||
HWSIM_ATTR_RX_RATE,
|
||||
HWSIM_ATTR_SIGNAL,
|
||||
HWSIM_ATTR_TX_INFO,
|
||||
HWSIM_ATTR_COOKIE,
|
||||
HWSIM_ATTR_CHANNELS,
|
||||
HWSIM_ATTR_RADIO_ID,
|
||||
HWSIM_ATTR_REG_HINT_ALPHA2,
|
||||
HWSIM_ATTR_REG_CUSTOM_REG,
|
||||
HWSIM_ATTR_REG_STRICT_REG,
|
||||
HWSIM_ATTR_SUPPORT_P2P_DEVICE,
|
||||
HWSIM_ATTR_USE_CHANCTX,
|
||||
HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE,
|
||||
HWSIM_ATTR_RADIO_NAME,
|
||||
HWSIM_ATTR_NO_VIF,
|
||||
HWSIM_ATTR_FREQ,
|
||||
HWSIM_ATTR_PAD,
|
||||
HWSIM_ATTR_TX_INFO_FLAGS,
|
||||
HWSIM_ATTR_PERM_ADDR,
|
||||
HWSIM_ATTR_IFTYPE_SUPPORT,
|
||||
HWSIM_ATTR_CIPHER_SUPPORT,
|
||||
};
|
||||
|
||||
struct hwsim_tx_rate {
|
||||
int8_t idx;
|
||||
uint8_t count;
|
||||
} __packed;
|
||||
static_assert(sizeof(hwsim_tx_rate) == 2);
|
||||
|
||||
#undef BIT
|
||||
@@ -154,16 +154,19 @@ static void toStream(std::stringstream& ss, const Buffer<nlattr> attr,
|
||||
}
|
||||
}
|
||||
|
||||
std::string toString(const Buffer<nlmsghdr> hdr, int protocol, bool printPayload) {
|
||||
if (!hdr.firstOk()) return "nlmsg{buffer overflow}";
|
||||
static void toStream(std::stringstream& ss, const Buffer<nlmsghdr> hdr, int protocol,
|
||||
bool printPayload) {
|
||||
if (!hdr.firstOk()) {
|
||||
ss << "nlmsg{buffer overflow}";
|
||||
return;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::setfill('0');
|
||||
|
||||
auto protocolMaybe = protocols::get(protocol);
|
||||
if (!protocolMaybe.has_value()) {
|
||||
ss << "nlmsg{protocol=" << protocol << "}";
|
||||
return ss.str();
|
||||
return;
|
||||
}
|
||||
protocols::NetlinkProtocol& protocolDescr = *protocolMaybe;
|
||||
|
||||
@@ -187,7 +190,7 @@ std::string toString(const Buffer<nlmsghdr> hdr, int protocol, bool printPayload
|
||||
ss << ", crc=" << std::hex << std::setw(4) << crc16(hdr.data<uint8_t>()) << std::dec;
|
||||
ss << '}';
|
||||
|
||||
if (!printPayload) return ss.str();
|
||||
if (!printPayload) return;
|
||||
ss << ' ';
|
||||
|
||||
if (!msgDescMaybe.has_value()) {
|
||||
@@ -210,6 +213,17 @@ std::string toString(const Buffer<nlmsghdr> hdr, int protocol, bool printPayload
|
||||
}
|
||||
|
||||
ss << "}";
|
||||
}
|
||||
|
||||
std::string toString(const Buffer<nlmsghdr> hdrs, int protocol, bool printPayload) {
|
||||
std::stringstream ss;
|
||||
bool first = true;
|
||||
for (const auto hdr : hdrs) {
|
||||
if (!first) ss << std::endl;
|
||||
first = false;
|
||||
|
||||
toStream(ss, hdr, protocol, printPayload);
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "Ctrl.h"
|
||||
|
||||
#include "families/Mac80211hwsim.h"
|
||||
#include "families/Nl80211.h"
|
||||
|
||||
#include <libnl++/Message.h>
|
||||
@@ -68,12 +69,15 @@ void Ctrl::track(const Buffer<nlmsghdr> hdr) {
|
||||
const auto familyId = msg.attributes.get<uint16_t>(CTRL_ATTR_FAMILY_ID);
|
||||
const auto familyName = msg.attributes.get<std::string>(CTRL_ATTR_FAMILY_NAME);
|
||||
|
||||
/* For now, we support just a single family. But if you add more, please define proper
|
||||
/* For now, we support just two families. But if you add more, please define proper
|
||||
* abstraction and not hardcode every name and class here.
|
||||
*/
|
||||
if (familyName == "nl80211") {
|
||||
mFamilyRegister[familyId] = std::make_shared<families::Nl80211>(familyId);
|
||||
}
|
||||
if (familyName == "MAC80211_HWSIM") {
|
||||
mFamilyRegister[familyId] = std::make_shared<families::Mac80211hwsim>(familyId);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android::nl::protocols::generic
|
||||
|
||||
@@ -40,9 +40,9 @@ void GenericMessageBase::toStream(std::stringstream& ss, const genlmsghdr& data)
|
||||
|
||||
ss << "genlmsghdr{";
|
||||
if (commandName.has_value()) {
|
||||
ss << "cmd=" << unsigned(data.cmd);
|
||||
} else {
|
||||
ss << "cmd=" << *commandName;
|
||||
} else {
|
||||
ss << "cmd=" << unsigned(data.cmd);
|
||||
}
|
||||
ss << ", version=" << unsigned(data.version);
|
||||
if (data.reserved != 0) ss << ", reserved=" << data.reserved;
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "Mac80211hwsim.h"
|
||||
|
||||
#include "../../structs.h"
|
||||
#include "common.h"
|
||||
|
||||
#include <libnl++/generic/families/mac80211_hwsim.h>
|
||||
|
||||
namespace android::nl::protocols::generic::families {
|
||||
|
||||
using DataType = AttributeDefinition::DataType;
|
||||
using Flags = AttributeDefinition::Flags;
|
||||
|
||||
static void hwsim_tx_rateToStream(std::stringstream& ss, const Buffer<nlattr> attr);
|
||||
|
||||
static const FlagsMap txControlFlags{
|
||||
{HWSIM_TX_CTL_REQ_TX_STATUS, "REQ_TX"},
|
||||
{HWSIM_TX_CTL_NO_ACK, "NO_ACK"},
|
||||
{HWSIM_TX_STAT_ACK, "ACK"},
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
Mac80211hwsim::Mac80211hwsim(nlmsgtype_t familyId) : GenericMessageBase(familyId, "hwsim", {
|
||||
{HWSIM_CMD_UNSPEC, "UNSPEC"},
|
||||
{HWSIM_CMD_REGISTER, "REGISTER"},
|
||||
{HWSIM_CMD_FRAME, "FRAME"},
|
||||
{HWSIM_CMD_TX_INFO_FRAME, "TX_INFO_FRAME"},
|
||||
{HWSIM_CMD_NEW_RADIO, "NEW_RADIO"},
|
||||
{HWSIM_CMD_DEL_RADIO, "DEL_RADIO"},
|
||||
{HWSIM_CMD_GET_RADIO, "GET_RADIO"},
|
||||
{HWSIM_CMD_ADD_MAC_ADDR, "ADD_MAC_ADDR"},
|
||||
{HWSIM_CMD_DEL_MAC_ADDR, "DEL_MAC_ADDR"},
|
||||
}, {
|
||||
{HWSIM_ATTR_UNSPEC, {"UNSPEC"}},
|
||||
{HWSIM_ATTR_ADDR_RECEIVER, {"ADDR_RECEIVER", DataType::Struct, hwaddrToStream}},
|
||||
{HWSIM_ATTR_ADDR_TRANSMITTER, {"ADDR_TRANSMITTER", DataType::Struct, hwaddrToStream}},
|
||||
{HWSIM_ATTR_FRAME, {"FRAME", DataType::Raw, AttributeMap{}, Flags::Verbose}},
|
||||
{HWSIM_ATTR_FLAGS, {"FLAGS", DataType::Struct, flagsToStream(txControlFlags)}},
|
||||
{HWSIM_ATTR_RX_RATE, {"RX_RATE", DataType::Uint}},
|
||||
{HWSIM_ATTR_SIGNAL, {"SIGNAL", DataType::Uint}},
|
||||
{HWSIM_ATTR_TX_INFO, {"TX_INFO", DataType::Struct, hwsim_tx_rateToStream}},
|
||||
{HWSIM_ATTR_COOKIE, {"COOKIE", DataType::Uint}},
|
||||
{HWSIM_ATTR_CHANNELS, {"CHANNELS", DataType::Uint}},
|
||||
{HWSIM_ATTR_RADIO_ID, {"RADIO_ID", DataType::Uint}},
|
||||
{HWSIM_ATTR_REG_HINT_ALPHA2, {"REG_HINT_ALPHA2", DataType::String}},
|
||||
{HWSIM_ATTR_REG_CUSTOM_REG, {"REG_CUSTOM_REG", DataType::Uint}},
|
||||
{HWSIM_ATTR_REG_STRICT_REG, {"REG_STRICT_REG", DataType::Flag}},
|
||||
{HWSIM_ATTR_SUPPORT_P2P_DEVICE, {"SUPPORT_P2P_DEVICE", DataType::Flag}},
|
||||
{HWSIM_ATTR_USE_CHANCTX, {"USE_CHANCTX", DataType::Flag}},
|
||||
{HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE, {"DESTROY_RADIO_ON_CLOSE", DataType::Flag}},
|
||||
{HWSIM_ATTR_RADIO_NAME, {"RADIO_NAME", DataType::String}},
|
||||
{HWSIM_ATTR_NO_VIF, {"NO_VIF", DataType::Flag}},
|
||||
{HWSIM_ATTR_FREQ, {"FREQ", DataType::Uint}},
|
||||
{HWSIM_ATTR_PAD, {"PAD", DataType::Uint}},
|
||||
{HWSIM_ATTR_TX_INFO_FLAGS, {"TX_INFO_FLAGS"}}, // hwsim_tx_rate_flag
|
||||
{HWSIM_ATTR_PERM_ADDR, {"PERM_ADDR"}},
|
||||
{HWSIM_ATTR_IFTYPE_SUPPORT, {"IFTYPE_SUPPORT", DataType::Uint}}, // NL80211_IFTYPE_STATION etc
|
||||
{HWSIM_ATTR_CIPHER_SUPPORT, {"CIPHER_SUPPORT", DataType::Struct, arrayToStream<int32_t>}},
|
||||
}) {}
|
||||
// clang-format on
|
||||
|
||||
static void hwsim_tx_rateToStream(std::stringstream& ss, const Buffer<nlattr> attr) {
|
||||
ss << '{';
|
||||
bool first = true;
|
||||
for (const auto rate : attr.data<hwsim_tx_rate>().getRaw()) {
|
||||
if (rate.idx == -1) continue;
|
||||
|
||||
ss << (int)rate.idx << ": " << (unsigned)rate.count;
|
||||
|
||||
if (!first) ss << ", ";
|
||||
first = false;
|
||||
}
|
||||
ss << '}';
|
||||
}
|
||||
|
||||
} // namespace android::nl::protocols::generic::families
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../GenericMessageBase.h"
|
||||
|
||||
namespace android::nl::protocols::generic::families {
|
||||
|
||||
class Mac80211hwsim : public GenericMessageBase {
|
||||
public:
|
||||
Mac80211hwsim(nlmsgtype_t familyId);
|
||||
};
|
||||
|
||||
} // namespace android::nl::protocols::generic::families
|
||||
57
automotive/can/1.0/default/libnl++/protocols/structs.cpp
Normal file
57
automotive/can/1.0/default/libnl++/protocols/structs.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "structs.h"
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
namespace android::nl::protocols {
|
||||
|
||||
AttributeDefinition::ToStream flagsToStream(FlagsMap flags) {
|
||||
return [flags](std::stringstream& ss, const Buffer<nlattr> attr) {
|
||||
auto val = attr.data<uint64_t>().copyFirst();
|
||||
|
||||
bool first = true;
|
||||
for (const auto& [flag, name] : flags) {
|
||||
if ((val & flag) != flag) continue;
|
||||
val &= ~flag;
|
||||
|
||||
if (!first) ss << '|';
|
||||
first = false;
|
||||
|
||||
ss << name;
|
||||
}
|
||||
|
||||
if (val == 0) return;
|
||||
|
||||
if (!first) ss << '|';
|
||||
ss << std::hex << val << std::dec;
|
||||
};
|
||||
}
|
||||
|
||||
void hwaddrToStream(std::stringstream& ss, const Buffer<nlattr> attr) {
|
||||
ss << std::hex;
|
||||
bool first = true;
|
||||
for (const auto byte : attr.data<uint8_t>().getRaw()) {
|
||||
if (!first) ss << ':';
|
||||
first = false;
|
||||
|
||||
ss << std::setw(2) << unsigned(byte);
|
||||
}
|
||||
ss << std::dec;
|
||||
}
|
||||
|
||||
} // namespace android::nl::protocols
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MessageDefinition.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace android::nl::protocols {
|
||||
@@ -30,4 +32,9 @@ void arrayToStream(std::stringstream& ss, const Buffer<nlattr> attr) {
|
||||
ss << '}';
|
||||
}
|
||||
|
||||
typedef std::map<uint64_t, std::string> FlagsMap;
|
||||
AttributeDefinition::ToStream flagsToStream(FlagsMap flags);
|
||||
|
||||
void hwaddrToStream(std::stringstream& ss, const Buffer<nlattr> attr);
|
||||
|
||||
} // namespace android::nl::protocols
|
||||
|
||||
42
automotive/can/1.0/tools/libprotocan/Android.bp
Normal file
42
automotive/can/1.0/tools/libprotocan/Android.bp
Normal file
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// Copyright (C) 2020 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package {
|
||||
// See: http://go/android-license-faq
|
||||
// A large-scale-change added 'default_applicable_licenses' to import
|
||||
// all of the 'license_kinds' from "hardware_interfaces_license"
|
||||
// to get the below license kinds:
|
||||
// SPDX-license-identifier-Apache-2.0
|
||||
default_applicable_licenses: ["hardware_interfaces_license"],
|
||||
}
|
||||
|
||||
cc_library_static {
|
||||
name: "libprotocan",
|
||||
defaults: ["android.hardware.automotive.can@defaults"],
|
||||
vendor: true,
|
||||
srcs: [
|
||||
"Checksum.cpp",
|
||||
"MessageCounter.cpp",
|
||||
"MessageDef.cpp",
|
||||
"MessageInjector.cpp",
|
||||
"Signal.cpp",
|
||||
],
|
||||
export_include_dirs: ["include"],
|
||||
|
||||
shared_libs: [
|
||||
"android.hardware.automotive.can@1.0",
|
||||
],
|
||||
}
|
||||
28
automotive/can/1.0/tools/libprotocan/Checksum.cpp
Normal file
28
automotive/can/1.0/tools/libprotocan/Checksum.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 <libprotocan/Checksum.h>
|
||||
|
||||
namespace android::hardware::automotive::protocan {
|
||||
|
||||
Checksum::Checksum(Signal signal, formula f) : mSignal(signal), mFormula(f) {}
|
||||
|
||||
void Checksum::update(can::V1_0::CanMessage& msg) const {
|
||||
mSignal.set(msg, 0);
|
||||
mSignal.set(msg, mFormula(msg) % (mSignal.maxValue + 1));
|
||||
}
|
||||
|
||||
} // namespace android::hardware::automotive::protocan
|
||||
61
automotive/can/1.0/tools/libprotocan/MessageCounter.cpp
Normal file
61
automotive/can/1.0/tools/libprotocan/MessageCounter.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 <libprotocan/MessageCounter.h>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
namespace android::hardware::automotive::protocan {
|
||||
|
||||
/** Whether to log counter state messages. */
|
||||
static constexpr bool kSuperVerbose = false;
|
||||
|
||||
MessageCounter::MessageCounter(Signal signal) : upperBound(signal.maxValue + 1), mSignal(signal) {}
|
||||
|
||||
Signal::value MessageCounter::next() const {
|
||||
CHECK(mCurrent.has_value()) << "Counter not initialized. Did you call isReady?";
|
||||
return (*mCurrent + 1) % upperBound;
|
||||
}
|
||||
|
||||
void MessageCounter::read(const can::V1_0::CanMessage& msg) {
|
||||
auto val = mSignal.get(msg);
|
||||
|
||||
if (!mCurrent.has_value()) {
|
||||
LOG(VERBOSE) << "Got first counter val of " << val;
|
||||
mCurrent = val;
|
||||
return;
|
||||
}
|
||||
|
||||
auto nextVal = next();
|
||||
if (nextVal == val) {
|
||||
if constexpr (kSuperVerbose) {
|
||||
LOG(VERBOSE) << "Got next counter val of " << nextVal;
|
||||
}
|
||||
mCurrent = nextVal;
|
||||
} else {
|
||||
LOG(DEBUG) << "Ignoring next counter val of " << val << ", waiting for " << nextVal;
|
||||
}
|
||||
}
|
||||
|
||||
bool MessageCounter::isReady() const { return mCurrent.has_value(); }
|
||||
|
||||
void MessageCounter::increment(can::V1_0::CanMessage& msg) {
|
||||
auto newVal = next();
|
||||
mCurrent = newVal;
|
||||
mSignal.set(msg, newVal);
|
||||
}
|
||||
|
||||
} // namespace android::hardware::automotive::protocan
|
||||
62
automotive/can/1.0/tools/libprotocan/MessageDef.cpp
Normal file
62
automotive/can/1.0/tools/libprotocan/MessageDef.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 <libprotocan/MessageDef.h>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
namespace android::hardware::automotive::protocan {
|
||||
|
||||
using can::V1_0::CanMessage;
|
||||
using can::V1_0::CanMessageId;
|
||||
|
||||
MessageDef::MessageDef(CanMessageId id, uint16_t len, std::map<std::string, Signal> signals,
|
||||
std::optional<Signal> counter, std::optional<Checksum> checksum)
|
||||
: id(id), kLen(len), kSignals(std::move(signals)), kCounter(counter), kChecksum(checksum) {}
|
||||
|
||||
const Signal& MessageDef::operator[](const std::string& signalName) const {
|
||||
auto it = kSignals.find(signalName);
|
||||
CHECK(it != kSignals.end()) << "Signal " << signalName << " doesn't exist";
|
||||
return it->second;
|
||||
}
|
||||
|
||||
CanMessage MessageDef::makeDefault() const {
|
||||
CanMessage msg = {};
|
||||
msg.id = id;
|
||||
msg.payload.resize(kLen);
|
||||
|
||||
for (auto const& [name, signal] : kSignals) {
|
||||
signal.setDefault(msg);
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
MessageCounter MessageDef::makeCounter() const {
|
||||
CHECK(kCounter.has_value()) << "Can't build a counter for message without such signal";
|
||||
return MessageCounter(*kCounter);
|
||||
}
|
||||
|
||||
void MessageDef::updateChecksum(can::V1_0::CanMessage& msg) const {
|
||||
if (!kChecksum.has_value()) return;
|
||||
kChecksum->update(msg);
|
||||
}
|
||||
|
||||
bool MessageDef::validate(const can::V1_0::CanMessage& msg) const {
|
||||
return msg.payload.size() >= kLen;
|
||||
}
|
||||
|
||||
} // namespace android::hardware::automotive::protocan
|
||||
110
automotive/can/1.0/tools/libprotocan/MessageInjector.cpp
Normal file
110
automotive/can/1.0/tools/libprotocan/MessageInjector.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 <libprotocan/MessageInjector.h>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace android::hardware::automotive::protocan {
|
||||
|
||||
/** Whether to log injected messages. */
|
||||
static constexpr bool kSuperVerbose = true;
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
using can::V1_0::CanMessage;
|
||||
using can::V1_0::CanMessageId;
|
||||
using can::V1_0::ICanBus;
|
||||
using can::V1_0::Result;
|
||||
|
||||
MessageInjector::MessageInjector(MessageDef msgDef,
|
||||
std::optional<std::chrono::milliseconds> interMessageDelay)
|
||||
: kMsgDef(std::move(msgDef)),
|
||||
kInterMessageDelay(interMessageDelay),
|
||||
mCounter(msgDef.makeCounter()) {}
|
||||
|
||||
void MessageInjector::inject(const CanMessage& msg) { inject({msg}); }
|
||||
|
||||
void MessageInjector::inject(const std::initializer_list<can::V1_0::CanMessage> msgs) {
|
||||
std::lock_guard<std::mutex> lock(mMessagesGuard);
|
||||
for (const auto& msg : msgs) {
|
||||
if constexpr (kSuperVerbose) {
|
||||
LOG(VERBOSE) << "Message scheduled for injection: " << toString(msg);
|
||||
}
|
||||
|
||||
mMessages.push(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageInjector::processQueueLocked(can::V1_0::ICanBus& bus) {
|
||||
if (mMessages.empty() || !mCounter.isReady()) return;
|
||||
|
||||
auto paddingMessagesCount = mCounter.upperBound - (mMessages.size() % mCounter.upperBound);
|
||||
auto padMessage = kMsgDef.makeDefault();
|
||||
for (unsigned i = 0; i < paddingMessagesCount; i++) {
|
||||
mMessages.push(padMessage);
|
||||
}
|
||||
|
||||
while (!mMessages.empty()) {
|
||||
auto&& outMsg = mMessages.front();
|
||||
|
||||
mCounter.increment(outMsg);
|
||||
kMsgDef.updateChecksum(outMsg);
|
||||
|
||||
if constexpr (kSuperVerbose) {
|
||||
LOG(VERBOSE) << "Injecting message: " << toString(outMsg);
|
||||
}
|
||||
auto result = bus.send(outMsg);
|
||||
if (result != Result::OK) {
|
||||
LOG(ERROR) << "Message injection failed: " << toString(result);
|
||||
}
|
||||
|
||||
mMessages.pop();
|
||||
|
||||
// This would block onReceive, but the class is not supposed to be used in production anyway
|
||||
// (see MessageInjector docstring).
|
||||
if (kInterMessageDelay.has_value()) {
|
||||
std::this_thread::sleep_for(*kInterMessageDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageInjector::onReceive(ICanBus& bus, const CanMessage& msg) {
|
||||
if (!kMsgDef.validate(msg)) return;
|
||||
|
||||
std::lock_guard<std::mutex> lock(mMessagesGuard);
|
||||
|
||||
mCounter.read(msg);
|
||||
processQueueLocked(bus);
|
||||
}
|
||||
|
||||
MessageInjectorManager::MessageInjectorManager(
|
||||
std::initializer_list<std::shared_ptr<MessageInjector>> injectors) {
|
||||
std::transform(injectors.begin(), injectors.end(), std::inserter(mInjectors, mInjectors.end()),
|
||||
[](const std::shared_ptr<MessageInjector>& injector) {
|
||||
return std::make_pair(injector->kMsgDef.id, std::move(injector));
|
||||
});
|
||||
}
|
||||
|
||||
void MessageInjectorManager::onReceive(sp<ICanBus> bus, const CanMessage& msg) {
|
||||
auto it = mInjectors.find(msg.id);
|
||||
if (it == mInjectors.end()) return;
|
||||
it->second->onReceive(*bus, msg);
|
||||
}
|
||||
|
||||
} // namespace android::hardware::automotive::protocan
|
||||
84
automotive/can/1.0/tools/libprotocan/Signal.cpp
Normal file
84
automotive/can/1.0/tools/libprotocan/Signal.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 <libprotocan/Signal.h>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
namespace android::hardware::automotive::protocan {
|
||||
|
||||
static uint8_t calculateLastByteMask(uint16_t start, uint8_t length) {
|
||||
unsigned lastByteBits = (start + length) % 8;
|
||||
unsigned lastBytePadding = (8 - lastByteBits) % 8;
|
||||
return 0xFF >> lastBytePadding;
|
||||
}
|
||||
|
||||
static uint8_t calculateFirstByteMask(uint16_t firstByte, uint8_t firstBit, uint16_t lastByte,
|
||||
uint8_t lastMask) {
|
||||
uint8_t firstMask = 0xFF << firstBit;
|
||||
if (firstByte == lastByte) firstMask &= lastMask;
|
||||
return firstMask;
|
||||
}
|
||||
|
||||
Signal::Signal(uint16_t start, uint8_t length, value defVal)
|
||||
: maxValue((1u << length) - 1),
|
||||
kFirstByte(start / 8),
|
||||
kFirstBit(start % 8),
|
||||
kFirstByteBits(8 - kFirstBit),
|
||||
kLastByte((start + length - 1) / 8),
|
||||
kLastMask(calculateLastByteMask(start, length)),
|
||||
kFirstMask(calculateFirstByteMask(kFirstByte, kFirstBit, kLastByte, kLastMask)),
|
||||
kDefVal(defVal) {
|
||||
CHECK(length > 0) << "Signal length must not be zero";
|
||||
}
|
||||
|
||||
Signal::value Signal::get(const can::V1_0::CanMessage& msg) const {
|
||||
CHECK(msg.payload.size() > kLastByte)
|
||||
<< "Message is too short. Did you call MessageDef::validate?";
|
||||
|
||||
Signal::value v = 0;
|
||||
if (kLastByte != kFirstByte) v = kLastMask & msg.payload[kLastByte];
|
||||
|
||||
for (int i = kLastByte - 1; i > kFirstByte; i--) {
|
||||
v = (v << 8) | msg.payload[i];
|
||||
}
|
||||
|
||||
return (v << kFirstByteBits) | ((msg.payload[kFirstByte] & kFirstMask) >> kFirstBit);
|
||||
}
|
||||
|
||||
void Signal::set(can::V1_0::CanMessage& msg, Signal::value val) const {
|
||||
CHECK(msg.payload.size() > kLastByte)
|
||||
<< "Signal requires message of length " << (kLastByte + 1)
|
||||
<< " which is beyond message length of " << msg.payload.size();
|
||||
|
||||
uint8_t firstByte = val << kFirstBit;
|
||||
val >>= kFirstByteBits;
|
||||
|
||||
msg.payload[kFirstByte] = (msg.payload[kFirstByte] & ~kFirstMask) | (firstByte & kFirstMask);
|
||||
|
||||
for (int i = kFirstByte + 1; i < kLastByte; i++) {
|
||||
msg.payload[i] = val & 0xFF;
|
||||
val >>= 8;
|
||||
}
|
||||
|
||||
if (kLastByte != kFirstByte) {
|
||||
msg.payload[kLastByte] = (msg.payload[kLastByte] & ~kLastMask) | (val & kLastMask);
|
||||
}
|
||||
}
|
||||
|
||||
void Signal::setDefault(can::V1_0::CanMessage& msg) const { set(msg, kDefVal); }
|
||||
|
||||
} // namespace android::hardware::automotive::protocan
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android/hardware/automotive/can/1.0/types.h>
|
||||
#include <libprotocan/Signal.h>
|
||||
|
||||
namespace android::hardware::automotive::protocan {
|
||||
|
||||
class Checksum {
|
||||
public:
|
||||
using formula = std::function<Signal::value(const can::V1_0::CanMessage&)>;
|
||||
|
||||
Checksum(Signal signal, formula f);
|
||||
|
||||
void update(can::V1_0::CanMessage& msg) const;
|
||||
|
||||
private:
|
||||
const Signal mSignal;
|
||||
const formula mFormula;
|
||||
};
|
||||
|
||||
} // namespace android::hardware::automotive::protocan
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android/hardware/automotive/can/1.0/types.h>
|
||||
#include <libprotocan/Signal.h>
|
||||
|
||||
namespace android::hardware::automotive::protocan {
|
||||
|
||||
class MessageCounter {
|
||||
public:
|
||||
const Signal::value upperBound;
|
||||
|
||||
MessageCounter(Signal signal);
|
||||
|
||||
/**
|
||||
* Parse CAN message sent by external ECU to determine current counter value.
|
||||
*/
|
||||
void read(const can::V1_0::CanMessage& msg);
|
||||
|
||||
/**
|
||||
* States whether current counter value is determined.
|
||||
*/
|
||||
bool isReady() const;
|
||||
|
||||
/**
|
||||
* Increment current counter value and set it in a new message.
|
||||
*
|
||||
* Caller must check isReady() at least once before calling this method.
|
||||
*/
|
||||
void increment(can::V1_0::CanMessage& msg);
|
||||
|
||||
private:
|
||||
const Signal mSignal;
|
||||
|
||||
std::optional<Signal::value> mCurrent = std::nullopt;
|
||||
|
||||
Signal::value next() const;
|
||||
};
|
||||
|
||||
} // namespace android::hardware::automotive::protocan
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android/hardware/automotive/can/1.0/types.h>
|
||||
#include <libprotocan/Checksum.h>
|
||||
#include <libprotocan/MessageCounter.h>
|
||||
#include <libprotocan/Signal.h>
|
||||
|
||||
namespace android::hardware::automotive::protocan {
|
||||
|
||||
/**
|
||||
* CAN message definition (not the actual message data).
|
||||
*
|
||||
* Describes static message properties (message ID, signals etc).
|
||||
*/
|
||||
class MessageDef {
|
||||
public:
|
||||
const can::V1_0::CanMessageId id;
|
||||
|
||||
/**
|
||||
* Create message definition.
|
||||
*
|
||||
* Currently only constant length messages are supported.
|
||||
*
|
||||
* \param id CAN message ID
|
||||
* \param len CAN message length
|
||||
* \param signals CAN signal definitions
|
||||
* \param counter Designated CAN signal definition for message counter, if the message has one
|
||||
* \param checksum Designated CAN signal definition for payload checksum, if the message has one
|
||||
*/
|
||||
MessageDef(can::V1_0::CanMessageId id, uint16_t len, std::map<std::string, Signal> signals,
|
||||
std::optional<Signal> counter = std::nullopt,
|
||||
std::optional<Checksum> checksum = std::nullopt);
|
||||
|
||||
const Signal& operator[](const std::string& signalName) const;
|
||||
|
||||
can::V1_0::CanMessage makeDefault() const;
|
||||
MessageCounter makeCounter() const;
|
||||
|
||||
void updateChecksum(can::V1_0::CanMessage& msg) const;
|
||||
|
||||
/**
|
||||
* Validate the message payload is large enough to hold all the signals.
|
||||
*/
|
||||
bool validate(const can::V1_0::CanMessage& msg) const;
|
||||
|
||||
private:
|
||||
const uint16_t kLen;
|
||||
const std::map<std::string, Signal> kSignals;
|
||||
const std::optional<Signal> kCounter;
|
||||
const std::optional<Checksum> kChecksum;
|
||||
};
|
||||
|
||||
} // namespace android::hardware::automotive::protocan
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android-base/macros.h>
|
||||
#include <android/hardware/automotive/can/1.0/ICanBus.h>
|
||||
#include <libprotocan/MessageCounter.h>
|
||||
#include <libprotocan/MessageDef.h>
|
||||
#include <utils/Mutex.h>
|
||||
|
||||
#include <queue>
|
||||
|
||||
namespace android::hardware::automotive::protocan {
|
||||
|
||||
class MessageInjectorManager;
|
||||
|
||||
/**
|
||||
* Injects CAN messages with a counter to an existing system.
|
||||
*
|
||||
* This class is NOT meant to use in production - there should be no need to inject counted CAN
|
||||
* messages where the other sender is also broadcasting them. If this is the case, it may be a sign
|
||||
* your CAN network needs a redesign. This tool is intended for use for testing and demo purposes.
|
||||
*/
|
||||
class MessageInjector {
|
||||
public:
|
||||
MessageInjector(MessageDef msgDef, std::optional<std::chrono::milliseconds> interMessageDelay);
|
||||
|
||||
void inject(const can::V1_0::CanMessage& msg);
|
||||
void inject(const std::initializer_list<can::V1_0::CanMessage> msgs);
|
||||
|
||||
private:
|
||||
const MessageDef kMsgDef;
|
||||
const std::optional<std::chrono::milliseconds> kInterMessageDelay;
|
||||
MessageCounter mCounter;
|
||||
|
||||
mutable std::mutex mMessagesGuard;
|
||||
std::queue<can::V1_0::CanMessage> mMessages GUARDED_BY(mMessagesGuard);
|
||||
|
||||
void onReceive(can::V1_0::ICanBus& bus, const can::V1_0::CanMessage& msg);
|
||||
void processQueueLocked(can::V1_0::ICanBus& bus);
|
||||
|
||||
friend class MessageInjectorManager;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MessageInjector);
|
||||
};
|
||||
|
||||
/**
|
||||
* Routes intercepted messages to MessageInjector instances configured to handle specific CAN
|
||||
* message (CAN message ID). Intercepted messages from other nodes in CAN network are used to read
|
||||
* current counter value in order to spoof the next packet.
|
||||
*/
|
||||
class MessageInjectorManager {
|
||||
public:
|
||||
MessageInjectorManager(std::initializer_list<std::shared_ptr<MessageInjector>> injectors);
|
||||
|
||||
void onReceive(sp<can::V1_0::ICanBus> bus, const can::V1_0::CanMessage& msg);
|
||||
|
||||
private:
|
||||
std::map<can::V1_0::CanMessageId, std::shared_ptr<MessageInjector>> mInjectors;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MessageInjectorManager);
|
||||
};
|
||||
|
||||
} // namespace android::hardware::automotive::protocan
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android-base/macros.h>
|
||||
#include <android/hardware/automotive/can/1.0/types.h>
|
||||
|
||||
namespace android::hardware::automotive::protocan {
|
||||
|
||||
/**
|
||||
* TODO(twasilczyk): right now, only Little Endian signals are supported.
|
||||
*/
|
||||
class Signal {
|
||||
public:
|
||||
using value = uint64_t;
|
||||
|
||||
const value maxValue;
|
||||
|
||||
Signal(uint16_t start, uint8_t length, value defVal = 0);
|
||||
|
||||
value get(const can::V1_0::CanMessage& msg) const;
|
||||
void set(can::V1_0::CanMessage& msg, value val) const;
|
||||
void setDefault(can::V1_0::CanMessage& msg) const;
|
||||
|
||||
private:
|
||||
const uint16_t kFirstByte; ///< Index of first byte that holds the signal
|
||||
const uint8_t kFirstBit; ///< Index of first bit within first byte
|
||||
const uint8_t kFirstByteBits; ///< How many bits of the first byte belong to the signal
|
||||
const uint16_t kLastByte; ///< Index of last byte that holds the signal
|
||||
const uint8_t kLastMask; ///< Bits of the last byte that belong to the signal
|
||||
const uint8_t kFirstMask; ///< Bits of the first byte that belong to the signal
|
||||
|
||||
const value kDefVal;
|
||||
};
|
||||
|
||||
} // namespace android::hardware::automotive::protocan
|
||||
39
automotive/can/1.0/tools/libprotocan/tests/Android.bp
Normal file
39
automotive/can/1.0/tools/libprotocan/tests/Android.bp
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// Copyright (C) 2020 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package {
|
||||
// See: http://go/android-license-faq
|
||||
// A large-scale-change added 'default_applicable_licenses' to import
|
||||
// all of the 'license_kinds' from "hardware_interfaces_license"
|
||||
// to get the below license kinds:
|
||||
// SPDX-license-identifier-Apache-2.0
|
||||
default_applicable_licenses: ["hardware_interfaces_license"],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "libprotocan_signal_test",
|
||||
defaults: ["android.hardware.automotive.can@defaults"],
|
||||
vendor: true,
|
||||
gtest: true,
|
||||
srcs: ["libprotocan_signal_test.cpp"],
|
||||
static_libs: [
|
||||
"libprotocan",
|
||||
],
|
||||
shared_libs: [
|
||||
"android.hardware.automotive.can@1.0",
|
||||
"libhidlbase",
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 <libprotocan/Signal.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace android::hardware::automotive::protocan::unittest {
|
||||
|
||||
TEST(SignalTest, TestGetSingleBytes) {
|
||||
can::V1_0::CanMessage msg = {};
|
||||
msg.payload = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
for (unsigned i = 0; i < msg.payload.size(); i++) {
|
||||
Signal signal(8 * i, 8);
|
||||
ASSERT_EQ(i, signal.get(msg));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SignalTest, TestSetSingleBytes) {
|
||||
std::vector<can::V1_0::CanMessage> msgs = {{}, {}, {}};
|
||||
msgs[0].payload = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||
msgs[1].payload = {0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB};
|
||||
msgs[2].payload = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
for (unsigned i = 0; i < msgs[0].payload.size(); i++) {
|
||||
Signal signal(8 * i, 8);
|
||||
|
||||
for (auto&& msgOriginal : msgs) {
|
||||
auto msgModified = msgOriginal;
|
||||
signal.set(msgModified, 0xBA);
|
||||
|
||||
auto msgExpected = msgOriginal;
|
||||
msgExpected.payload[i] = 0xBA;
|
||||
|
||||
ASSERT_EQ(msgExpected, msgModified) << "i=" << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SignalTest, TestGetStart4) {
|
||||
/* Data generated with Python3:
|
||||
*
|
||||
* from cantools.database.can import *
|
||||
* hex(Message(1, 'm', 4, [Signal('s', 4, 16, byte_order='little_endian')]).
|
||||
* decode(b'\xde\xad\xbe\xef')['s'])
|
||||
*/
|
||||
|
||||
can::V1_0::CanMessage msg = {};
|
||||
msg.payload = {0xDE, 0xAD, 0xBE, 0xEF};
|
||||
can::V1_0::CanMessage msg2 = {};
|
||||
msg2.payload = {0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD};
|
||||
|
||||
Signal s0_4(0, 4);
|
||||
Signal s4_4(4, 4);
|
||||
Signal s4_8(4, 8);
|
||||
Signal s4_16(4, 16);
|
||||
Signal s4_28(4, 28);
|
||||
Signal s12_8(12, 8);
|
||||
Signal s12_12(12, 12);
|
||||
Signal s12_16(12, 16);
|
||||
Signal s12_20(12, 20);
|
||||
Signal s12_32(12, 32);
|
||||
|
||||
ASSERT_EQ(0xEu, s0_4.get(msg));
|
||||
ASSERT_EQ(0xDu, s4_4.get(msg));
|
||||
ASSERT_EQ(0xDDu, s4_8.get(msg));
|
||||
ASSERT_EQ(0xEADDu, s4_16.get(msg));
|
||||
ASSERT_EQ(0xEFBEADDu, s4_28.get(msg));
|
||||
ASSERT_EQ(0xEAu, s12_8.get(msg));
|
||||
ASSERT_EQ(0xBEAu, s12_12.get(msg));
|
||||
ASSERT_EQ(0xFBEAu, s12_16.get(msg));
|
||||
ASSERT_EQ(0xEFBEAu, s12_20.get(msg));
|
||||
ASSERT_EQ(0xDDEEFBEAu, s12_32.get(msg2));
|
||||
}
|
||||
|
||||
TEST(SignalTest, TestGet64) {
|
||||
/* Data generated with Python3:
|
||||
*
|
||||
* from cantools.database.can import *
|
||||
* hex(Message(1, 'm', 9, [Signal('s', 4, 64, byte_order='little_endian')]).
|
||||
* decode(b'\xde\xad\xbe\xef\xab\xbc\xcd\xde\xef')['s'])
|
||||
*/
|
||||
|
||||
can::V1_0::CanMessage msg = {};
|
||||
msg.payload = {0xDE, 0xAD, 0xBE, 0xEF, 0xAB, 0xBC, 0xCD, 0xDE, 0xEF};
|
||||
|
||||
Signal s0_64(0, 64);
|
||||
Signal s8_64(8, 64);
|
||||
Signal s4_64(4, 64);
|
||||
Signal s1_64(1, 64);
|
||||
|
||||
ASSERT_EQ(0xDECDBCABEFBEADDEu, s0_64.get(msg));
|
||||
ASSERT_EQ(0xEFDECDBCABEFBEADu, s8_64.get(msg));
|
||||
ASSERT_EQ(0xFDECDBCABEFBEADDu, s4_64.get(msg));
|
||||
ASSERT_EQ(0xEF66DE55F7DF56EFu, s1_64.get(msg));
|
||||
}
|
||||
|
||||
TEST(SignalTest, TestGetAllStarts) {
|
||||
/* Data generated with Python3:
|
||||
*
|
||||
* from cantools.database.can import *
|
||||
* hex(Message(1, 'm', 6, [Signal('s', 0, 20, byte_order='little_endian')]).
|
||||
* decode(b'\xde\xad\xbe\xef\xde\xad')['s'])
|
||||
*/
|
||||
|
||||
std::map<int, Signal::value> shifts = {
|
||||
{0, 0xEADDEu}, {1, 0xF56EFu}, {2, 0xFAB77u}, {3, 0x7D5BBu}, {4, 0xBEADDu}, {5, 0xDF56Eu},
|
||||
{6, 0xEFAB7u}, {7, 0xF7D5Bu}, {8, 0xFBEADu}, {9, 0x7DF56u}, {10, 0xBEFABu}, {11, 0xDF7D5u},
|
||||
};
|
||||
|
||||
can::V1_0::CanMessage msg = {};
|
||||
msg.payload = {0xDE, 0xAD, 0xBE, 0xEF, 0xCC, 0xCC};
|
||||
|
||||
for (auto const& [start, expected] : shifts) {
|
||||
Signal s(start, 20);
|
||||
ASSERT_EQ(expected, s.get(msg)) << "shift of " << start << " failed";
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SignalTest, TestSetStart4) {
|
||||
/* Data generated with Python3:
|
||||
*
|
||||
* from cantools.database.can import *
|
||||
* so=4 ; sl=8
|
||||
* md = Message(1, 'm', 4, [Signal('a1', 0, so), Signal('a2', so+sl, 32-so-sl),
|
||||
* Signal('s', so, sl, byte_order='little_endian')])
|
||||
* m = md.decode(b'\xcc\xcc\xcc\xcc')
|
||||
* m['s'] = 0xDE
|
||||
* binascii.hexlify(md.encode(m)).upper()
|
||||
*/
|
||||
typedef struct {
|
||||
int start;
|
||||
int length;
|
||||
Signal::value setValue;
|
||||
hidl_vec<uint8_t> payload;
|
||||
} case_t;
|
||||
|
||||
std::vector<case_t> cases = {
|
||||
{0, 4, 0xDu, {0xCD, 0xCC, 0xCC, 0xCC}}, {4, 4, 0xDu, {0xDC, 0xCC, 0xCC, 0xCC}},
|
||||
{4, 8, 0xDEu, {0xEC, 0xCD, 0xCC, 0xCC}}, {4, 16, 0xDEADu, {0xDC, 0xEA, 0xCD, 0xCC}},
|
||||
{4, 24, 0xDEADBEu, {0xEC, 0xDB, 0xEA, 0xCD}}, {4, 28, 0xDEADBEEu, {0xEC, 0xBE, 0xAD, 0xDE}},
|
||||
{12, 8, 0xDEu, {0xCC, 0xEC, 0xCD, 0xCC}}, {12, 12, 0xDEAu, {0xCC, 0xAC, 0xDE, 0xCC}},
|
||||
{12, 16, 0xDEADu, {0xCC, 0xDC, 0xEA, 0xCD}}, {12, 20, 0xDEADBu, {0xCC, 0xBC, 0xAD, 0xDE}},
|
||||
};
|
||||
|
||||
can::V1_0::CanMessage msg = {};
|
||||
msg.payload = {0xCC, 0xCC, 0xCC, 0xCC};
|
||||
|
||||
for (auto const& tcase : cases) {
|
||||
Signal s(tcase.start, tcase.length);
|
||||
|
||||
can::V1_0::CanMessage expectedMsg = {};
|
||||
expectedMsg.payload = tcase.payload;
|
||||
|
||||
can::V1_0::CanMessage editedMsg = msg;
|
||||
s.set(editedMsg, tcase.setValue);
|
||||
|
||||
ASSERT_EQ(expectedMsg, editedMsg) << " set(" << tcase.start << ", " << tcase.length << ")";
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SignalTest, TestSetAllStarts) {
|
||||
/* Data generated with Python3:
|
||||
* from cantools.database.can import *
|
||||
* import binascii
|
||||
* import textwrap
|
||||
*
|
||||
* length = 20
|
||||
* for start in range(0, 32 - length):
|
||||
* signals = [Signal('s', start, length, byte_order='little_endian')]
|
||||
* if start > 0: signals.append(Signal('a', 0, start, byte_order='little_endian'))
|
||||
* signals.append(Signal('b', start + length, 32 - start - length,
|
||||
* byte_order='little_endian'))
|
||||
*
|
||||
* md = Message(1, 'm', 4, signals)
|
||||
* m = md.decode(b'\xcc\xcc\xcc\xcc')
|
||||
* m['s'] = 0xDEADB
|
||||
* out = binascii.hexlify(md.encode(m)).decode('ascii').upper()
|
||||
* out = ', '.join(['0x{}'.format(v) for v in textwrap.wrap(out, 2)])
|
||||
* print('{{ {:d}, {{ {:s} }}}},'.format(start, out))
|
||||
*/
|
||||
|
||||
std::map<int, hidl_vec<uint8_t>> shifts = {
|
||||
{0, {0xDB, 0xEA, 0xCD, 0xCC}}, {1, {0xB6, 0xD5, 0xDB, 0xCC}}, {2, {0x6C, 0xAB, 0xF7, 0xCC}},
|
||||
{3, {0xDC, 0x56, 0xEF, 0xCC}}, {4, {0xBC, 0xAD, 0xDE, 0xCC}}, {5, {0x6C, 0x5B, 0xBD, 0xCD}},
|
||||
{6, {0xCC, 0xB6, 0x7A, 0xCF}}, {7, {0xCC, 0x6D, 0xF5, 0xCE}}, {8, {0xCC, 0xDB, 0xEA, 0xCD}},
|
||||
{9, {0xCC, 0xB6, 0xD5, 0xDB}}, {10, {0xCC, 0x6C, 0xAB, 0xF7}}, {11, {0xCC, 0xDC, 0x56, 0xEF}},
|
||||
};
|
||||
|
||||
can::V1_0::CanMessage msg = {};
|
||||
msg.payload = {0xCC, 0xCC, 0xCC, 0xCC};
|
||||
|
||||
for (auto const& [start, expectedPayload] : shifts) {
|
||||
Signal s(start, 20);
|
||||
|
||||
can::V1_0::CanMessage expectedMsg = {};
|
||||
expectedMsg.payload = expectedPayload;
|
||||
|
||||
can::V1_0::CanMessage editedMsg = msg;
|
||||
s.set(editedMsg, 0xDEADB);
|
||||
|
||||
ASSERT_EQ(expectedMsg, editedMsg) << "shift of " << start << " failed";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android::hardware::automotive::protocan::unittest
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <android-base/strings.h>
|
||||
#include <android/hardware/automotive/can/1.0/ICanBus.h>
|
||||
#include <android/hardware/automotive/can/1.0/types.h>
|
||||
#include <can-vts-utils/bus-enumerator.h>
|
||||
#include <can-vts-utils/can-hal-printers.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <hidl-utils/hidl-utils.h>
|
||||
@@ -27,6 +28,8 @@
|
||||
namespace android::hardware::automotive::can::V1_0::vts {
|
||||
|
||||
using hardware::hidl_vec;
|
||||
using InterfaceType = ICanController::InterfaceType;
|
||||
using IfId = ICanController::BusConfig::InterfaceId;
|
||||
|
||||
struct CanMessageListener : public can::V1_0::ICanMessageListener {
|
||||
virtual Return<void> onReceive(const can::V1_0::CanMessage&) override { return {}; }
|
||||
@@ -41,22 +44,62 @@ class CanBusHalTest : public ::testing::TestWithParam<std::string> {
|
||||
virtual void SetUp() override;
|
||||
virtual void TearDown() override;
|
||||
|
||||
bool up(InterfaceType iftype, const std::string& srvname, const std::string& ifname);
|
||||
|
||||
std::tuple<Result, sp<ICloseHandle>> listen(const hidl_vec<CanMessageFilter>& filter,
|
||||
const sp<ICanMessageListener>& listener);
|
||||
sp<ICloseHandle> listenForErrors(const sp<ICanErrorListener>& listener);
|
||||
|
||||
sp<ICanBus> mCanBus;
|
||||
sp<ICanController> mCanController;
|
||||
};
|
||||
|
||||
void CanBusHalTest::SetUp() {
|
||||
mCanBus = ICanBus::getService(GetParam());
|
||||
ASSERT_TRUE(mCanBus) << "Couldn't open CAN Bus: " << GetParam();
|
||||
const auto controllers = getAllHalInstanceNames(ICanController::descriptor);
|
||||
ASSERT_GT(controllers.size(), 0u);
|
||||
// just grab the first one
|
||||
mCanController = ICanController::getService(controllers[0]);
|
||||
ASSERT_TRUE(mCanController) << "Couldn't open CAN Controller: " << controllers[0];
|
||||
|
||||
// this will throw an error if the bus is already up, but we have to try.
|
||||
up(InterfaceType::VIRTUAL, GetParam(), "vcan0");
|
||||
}
|
||||
|
||||
void CanBusHalTest::TearDown() {
|
||||
mCanBus.clear();
|
||||
}
|
||||
|
||||
bool CanBusHalTest::up(InterfaceType iftype, const std::string& srvname,
|
||||
const std::string& ifname) {
|
||||
ICanController::BusConfig config = {};
|
||||
config.name = srvname;
|
||||
|
||||
// TODO(b/146214370): move interfaceId constructors to a library
|
||||
if (iftype == InterfaceType::SOCKETCAN) {
|
||||
IfId::Socketcan socketcan = {};
|
||||
socketcan.ifname(ifname);
|
||||
config.interfaceId.socketcan(socketcan);
|
||||
} else if (iftype == InterfaceType::SLCAN) {
|
||||
IfId::Slcan slcan = {};
|
||||
slcan.ttyname(ifname);
|
||||
config.interfaceId.slcan(slcan);
|
||||
} else if (iftype == InterfaceType::VIRTUAL) {
|
||||
config.interfaceId.virtualif({ifname});
|
||||
} else {
|
||||
ADD_FAILURE() << "Unexpected iftype: " << toString(iftype);
|
||||
}
|
||||
|
||||
const auto upresult = mCanController->upInterface(config);
|
||||
if (upresult != ICanController::Result::OK) {
|
||||
// upInterface returns INVALID_STATE if the interface is already up (which is fine).
|
||||
EXPECT_EQ(ICanController::Result::INVALID_STATE, upresult)
|
||||
<< ifname << " can't be brought up!";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::tuple<Result, sp<ICloseHandle>> CanBusHalTest::listen(
|
||||
const hidl_vec<CanMessageFilter>& filter, const sp<ICanMessageListener>& listener) {
|
||||
Result halResult;
|
||||
|
||||
@@ -33,18 +33,45 @@ using hardware::hidl_vec;
|
||||
using InterfaceType = ICanController::InterfaceType;
|
||||
using IfId = ICanController::BusConfig::InterfaceId;
|
||||
|
||||
struct Bus {
|
||||
DISALLOW_COPY_AND_ASSIGN(Bus);
|
||||
|
||||
Bus(sp<ICanController> controller, const ICanController::BusConfig& config)
|
||||
: mIfname(config.name), mController(controller) {
|
||||
// Don't bring up the bus, we just need a wrapper for the ICanBus object
|
||||
/* Not using ICanBus::getService here, since it ignores interfaces not in the manifest
|
||||
* file -- this is a test, so we don't want to add fake services to a device manifest. */
|
||||
auto manager = hidl::manager::V1_2::IServiceManager::getService();
|
||||
auto service = manager->get(ICanBus::descriptor, config.name);
|
||||
mBus = ICanBus::castFrom(service);
|
||||
}
|
||||
|
||||
ICanBus* operator->() const { return mBus.get(); }
|
||||
sp<ICanBus> get() { return mBus; }
|
||||
|
||||
Return<Result> send(const CanMessage& msg) { return mBus->send(msg); }
|
||||
|
||||
private:
|
||||
const std::string mIfname;
|
||||
sp<ICanController> mController;
|
||||
sp<ICanBus> mBus;
|
||||
};
|
||||
|
||||
class CanControllerHalTest : public ::testing::TestWithParam<std::string> {
|
||||
protected:
|
||||
virtual void SetUp() override;
|
||||
virtual void TearDown() override;
|
||||
static void SetUpTestCase();
|
||||
|
||||
Bus makeBus(const std::string ifaceName);
|
||||
|
||||
hidl_vec<InterfaceType> getSupportedInterfaceTypes();
|
||||
bool isSupported(InterfaceType iftype);
|
||||
|
||||
bool up(InterfaceType iftype, const std::string srvname, std::string ifname,
|
||||
ICanController::Result expected);
|
||||
void assertRegistered(const std::string srvname, bool expectRegistered);
|
||||
void assertRegistered(const std::string srvname, const std::string ifaceName,
|
||||
bool expectRegistered);
|
||||
|
||||
sp<ICanController> mCanController;
|
||||
static hidl_vec<hidl_string> mBusNames;
|
||||
@@ -117,16 +144,33 @@ bool CanControllerHalTest::up(InterfaceType iftype, std::string srvname, std::st
|
||||
return true;
|
||||
}
|
||||
|
||||
void CanControllerHalTest::assertRegistered(std::string srvname, bool expectRegistered) {
|
||||
void CanControllerHalTest::assertRegistered(const std::string srvname, const std::string ifaceName,
|
||||
bool expectRegistered) {
|
||||
/* Not using ICanBus::tryGetService here, since it ignores interfaces not in the manifest
|
||||
* file -- this is a test, so we don't want to add fake services to a device manifest. */
|
||||
auto manager = hidl::manager::V1_2::IServiceManager::getService();
|
||||
auto busService = manager->get(ICanBus::descriptor, srvname);
|
||||
if (!expectRegistered) {
|
||||
/* We can't unregister a HIDL interface defined in the manifest, so we'll just check to make
|
||||
* sure that the interface behind it is down */
|
||||
auto bus = makeBus(ifaceName);
|
||||
const auto result = bus->send({});
|
||||
ASSERT_EQ(Result::INTERFACE_DOWN, result);
|
||||
return;
|
||||
}
|
||||
ASSERT_EQ(expectRegistered, busService.withDefault(nullptr) != nullptr)
|
||||
<< "ICanBus/" << srvname << (expectRegistered ? " is not " : " is ") << "registered"
|
||||
<< " (should be otherwise)";
|
||||
}
|
||||
|
||||
Bus CanControllerHalTest::makeBus(const std::string ifaceName) {
|
||||
ICanController::BusConfig config = {};
|
||||
config.name = mBusNames[0];
|
||||
config.interfaceId.virtualif({ifaceName});
|
||||
|
||||
return Bus(mCanController, config);
|
||||
}
|
||||
|
||||
TEST_P(CanControllerHalTest, SupportsSomething) {
|
||||
const auto supported = getSupportedInterfaceTypes();
|
||||
ASSERT_GT(supported.size(), 0u);
|
||||
@@ -134,15 +178,17 @@ TEST_P(CanControllerHalTest, SupportsSomething) {
|
||||
|
||||
TEST_P(CanControllerHalTest, BringUpDown) {
|
||||
const std::string name = mBusNames[0];
|
||||
const std::string iface = "vcan57";
|
||||
mCanController->downInterface(name);
|
||||
|
||||
assertRegistered(name, false);
|
||||
if (!up(InterfaceType::VIRTUAL, name, "vcan57", ICanController::Result::OK)) GTEST_SKIP();
|
||||
assertRegistered(name, true);
|
||||
assertRegistered(name, iface, false);
|
||||
|
||||
if (!up(InterfaceType::VIRTUAL, name, iface, ICanController::Result::OK)) GTEST_SKIP();
|
||||
assertRegistered(name, iface, true);
|
||||
|
||||
const auto dnresult = mCanController->downInterface(name);
|
||||
ASSERT_TRUE(dnresult);
|
||||
|
||||
assertRegistered(name, false);
|
||||
assertRegistered(name, iface, false);
|
||||
}
|
||||
|
||||
TEST_P(CanControllerHalTest, DownFake) {
|
||||
@@ -152,18 +198,19 @@ TEST_P(CanControllerHalTest, DownFake) {
|
||||
|
||||
TEST_P(CanControllerHalTest, UpTwice) {
|
||||
const std::string name = mBusNames[0];
|
||||
const std::string iface = "vcan72";
|
||||
|
||||
assertRegistered(name, false);
|
||||
if (!up(InterfaceType::VIRTUAL, name, "vcan72", ICanController::Result::OK)) GTEST_SKIP();
|
||||
assertRegistered(name, true);
|
||||
assertRegistered(name, iface, false);
|
||||
if (!up(InterfaceType::VIRTUAL, name, iface, ICanController::Result::OK)) GTEST_SKIP();
|
||||
assertRegistered(name, iface, true);
|
||||
if (!up(InterfaceType::VIRTUAL, name, "vcan73", ICanController::Result::INVALID_STATE)) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
assertRegistered(name, true);
|
||||
assertRegistered(name, iface, true);
|
||||
|
||||
const auto result = mCanController->downInterface(name);
|
||||
ASSERT_TRUE(result);
|
||||
assertRegistered(name, false);
|
||||
assertRegistered(name, iface, false);
|
||||
}
|
||||
|
||||
TEST_P(CanControllerHalTest, ConfigCompatibility) {
|
||||
@@ -230,63 +277,67 @@ TEST_P(CanControllerHalTest, ConfigCompatibility) {
|
||||
|
||||
TEST_P(CanControllerHalTest, FailEmptyName) {
|
||||
const std::string name = "";
|
||||
const std::string iface = "vcan57";
|
||||
|
||||
assertRegistered(name, false);
|
||||
if (!up(InterfaceType::VIRTUAL, name, "vcan57", ICanController::Result::BAD_SERVICE_NAME)) {
|
||||
assertRegistered(name, iface, false);
|
||||
if (!up(InterfaceType::VIRTUAL, name, iface, ICanController::Result::BAD_SERVICE_NAME)) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
assertRegistered(name, false);
|
||||
assertRegistered(name, iface, false);
|
||||
}
|
||||
|
||||
TEST_P(CanControllerHalTest, FailBadName) {
|
||||
// 33 characters (name can be at most 32 characters long)
|
||||
const std::string name = "ab012345678901234567890123456789c";
|
||||
const std::string iface = "vcan57";
|
||||
|
||||
assertRegistered(name, false);
|
||||
if (!up(InterfaceType::VIRTUAL, name, "vcan57", ICanController::Result::BAD_SERVICE_NAME)) {
|
||||
assertRegistered(name, iface, false);
|
||||
if (!up(InterfaceType::VIRTUAL, name, iface, ICanController::Result::BAD_SERVICE_NAME)) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
assertRegistered(name, false);
|
||||
assertRegistered(name, iface, false);
|
||||
}
|
||||
|
||||
TEST_P(CanControllerHalTest, FailBadVirtualAddress) {
|
||||
const std::string name = mBusNames[0];
|
||||
const std::string iface = "";
|
||||
|
||||
assertRegistered(name, false);
|
||||
if (!up(InterfaceType::VIRTUAL, name, "", ICanController::Result::BAD_INTERFACE_ID)) {
|
||||
assertRegistered(name, iface, false);
|
||||
if (!up(InterfaceType::VIRTUAL, name, iface, ICanController::Result::BAD_INTERFACE_ID)) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
assertRegistered(name, false);
|
||||
assertRegistered(name, iface, false);
|
||||
}
|
||||
|
||||
TEST_P(CanControllerHalTest, FailBadSocketcanAddress) {
|
||||
const std::string name = mBusNames[0];
|
||||
const std::string iface = "can87";
|
||||
|
||||
assertRegistered(name, false);
|
||||
if (!up(InterfaceType::SOCKETCAN, name, "can87", ICanController::Result::BAD_INTERFACE_ID)) {
|
||||
assertRegistered(name, iface, false);
|
||||
if (!up(InterfaceType::SOCKETCAN, name, iface, ICanController::Result::BAD_INTERFACE_ID)) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
assertRegistered(name, false);
|
||||
assertRegistered(name, iface, false);
|
||||
|
||||
auto supported =
|
||||
up(InterfaceType::SOCKETCAN, name, "", ICanController::Result::BAD_INTERFACE_ID);
|
||||
ASSERT_TRUE(supported);
|
||||
assertRegistered(name, false);
|
||||
assertRegistered(name, iface, false);
|
||||
}
|
||||
|
||||
TEST_P(CanControllerHalTest, FailBadSlcanAddress) {
|
||||
const std::string name = mBusNames[0];
|
||||
const std::string iface = "/dev/shouldnotexist123";
|
||||
|
||||
assertRegistered(name, false);
|
||||
if (!up(InterfaceType::SLCAN, name, "/dev/shouldnotexist123",
|
||||
ICanController::Result::BAD_INTERFACE_ID)) {
|
||||
assertRegistered(name, iface, false);
|
||||
if (!up(InterfaceType::SLCAN, name, iface, ICanController::Result::BAD_INTERFACE_ID)) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
assertRegistered(name, false);
|
||||
assertRegistered(name, iface, false);
|
||||
|
||||
auto supported = up(InterfaceType::SLCAN, name, "", ICanController::Result::BAD_INTERFACE_ID);
|
||||
ASSERT_TRUE(supported);
|
||||
assertRegistered(name, false);
|
||||
assertRegistered(name, iface, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,48 +13,46 @@ cc_binary {
|
||||
proprietary: true,
|
||||
relative_install_path: "hw",
|
||||
srcs: [
|
||||
"service.cpp",
|
||||
"EvsCamera.cpp",
|
||||
"EvsEnumerator.cpp",
|
||||
"EvsDisplay.cpp",
|
||||
"ConfigManager.cpp",
|
||||
"ConfigManagerUtil.cpp",
|
||||
"EvsUltrasonicsArray.cpp",
|
||||
"*.cpp",
|
||||
],
|
||||
init_rc: ["android.hardware.automotive.evs@1.1-service.rc"],
|
||||
|
||||
shared_libs: [
|
||||
"android.frameworks.automotive.display@1.0",
|
||||
"android.hardware.automotive.evs@1.0",
|
||||
"android.hardware.automotive.evs@1.1",
|
||||
"android.hardware.camera.device@3.3",
|
||||
"android.hardware.graphics.bufferqueue@1.0",
|
||||
"android.hardware.graphics.bufferqueue@2.0",
|
||||
"android.hidl.allocator@1.0",
|
||||
"android.hidl.memory@1.0",
|
||||
"android.hidl.token@1.0-utils",
|
||||
"libEGL",
|
||||
"libGLESv2",
|
||||
"libbase",
|
||||
"libbinder",
|
||||
"liblog",
|
||||
"libbufferqueueconverter",
|
||||
"libcamera_metadata",
|
||||
"libhardware",
|
||||
"libhidlbase",
|
||||
"libhidlmemory",
|
||||
"liblog",
|
||||
"libtinyxml2",
|
||||
"libui",
|
||||
"libutils",
|
||||
"libcamera_metadata",
|
||||
"libtinyxml2",
|
||||
"android.hidl.token@1.0-utils",
|
||||
"android.frameworks.automotive.display@1.0",
|
||||
"android.hardware.graphics.bufferqueue@1.0",
|
||||
"android.hardware.graphics.bufferqueue@2.0",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-O0",
|
||||
"-g",
|
||||
"-DLOG_TAG=\"MockEvsDriver\"",
|
||||
"-DGL_GLEXT_PROTOTYPES",
|
||||
"-DEGL_EGLEXT_PROTOTYPES",
|
||||
],
|
||||
include_dirs: [
|
||||
"frameworks/native/include/",
|
||||
],
|
||||
|
||||
required: [
|
||||
"evs_default_configuration.xml",
|
||||
],
|
||||
|
||||
vintf_fragments: [
|
||||
"manifest_android.hardware.automotive.evs@1.1-service.xml",
|
||||
],
|
||||
|
||||
@@ -14,38 +14,40 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <thread>
|
||||
|
||||
#include <hardware/gralloc.h>
|
||||
#include <utils/SystemClock.h>
|
||||
#include <android/hardware/camera/device/3.2/ICameraDevice.h>
|
||||
|
||||
#include "ConfigManager.h"
|
||||
|
||||
using ::android::hardware::camera::device::V3_2::StreamRotation;
|
||||
#include <android/hardware/camera/device/3.2/ICameraDevice.h>
|
||||
#include <hardware/gralloc.h>
|
||||
#include <utils/SystemClock.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
|
||||
namespace android::hardware::automotive::evs::V1_1::implementation {
|
||||
|
||||
using namespace std;
|
||||
using namespace tinyxml2;
|
||||
using hardware::camera::device::V3_2::StreamRotation;
|
||||
|
||||
ConfigManager::~ConfigManager() {
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
|
||||
void ConfigManager::readCameraInfo(const XMLElement * const aCameraElem) {
|
||||
void ConfigManager::readCameraInfo(const XMLElement* const aCameraElem) {
|
||||
if (aCameraElem == nullptr) {
|
||||
ALOGW("XML file does not have required camera element");
|
||||
return;
|
||||
}
|
||||
|
||||
const XMLElement *curElem = aCameraElem->FirstChildElement();
|
||||
const XMLElement* curElem = aCameraElem->FirstChildElement();
|
||||
while (curElem != nullptr) {
|
||||
if (!strcmp(curElem->Name(), "group")) {
|
||||
/* camera group identifier */
|
||||
const char *id = curElem->FindAttribute("id")->Value();
|
||||
const char* id = curElem->FindAttribute("id")->Value();
|
||||
|
||||
/* create a camera group to be filled */
|
||||
CameraGroupInfo *aCamera = new CameraGroupInfo();
|
||||
CameraGroupInfo* aCamera = new CameraGroupInfo();
|
||||
|
||||
/* read camera device information */
|
||||
if (!readCameraDeviceInfo(aCamera, curElem)) {
|
||||
@@ -55,28 +57,26 @@ void ConfigManager::readCameraInfo(const XMLElement * const aCameraElem) {
|
||||
}
|
||||
|
||||
/* camera group synchronization */
|
||||
const char *sync = curElem->FindAttribute("synchronized")->Value();
|
||||
const char* sync = curElem->FindAttribute("synchronized")->Value();
|
||||
if (!strcmp(sync, "CALIBRATED")) {
|
||||
aCamera->synchronized =
|
||||
ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED;
|
||||
aCamera->synchronized = ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED;
|
||||
} else if (!strcmp(sync, "APPROXIMATE")) {
|
||||
aCamera->synchronized =
|
||||
ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE;
|
||||
aCamera->synchronized = ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE;
|
||||
} else {
|
||||
aCamera->synchronized = 0; // Not synchronized
|
||||
aCamera->synchronized = 0; // Not synchronized
|
||||
}
|
||||
|
||||
/* add a group to hash map */
|
||||
mCameraGroupInfos.insert_or_assign(id, unique_ptr<CameraGroupInfo>(aCamera));
|
||||
} else if (!strcmp(curElem->Name(), "device")) {
|
||||
/* camera unique identifier */
|
||||
const char *id = curElem->FindAttribute("id")->Value();
|
||||
const char* id = curElem->FindAttribute("id")->Value();
|
||||
|
||||
/* camera mount location */
|
||||
const char *pos = curElem->FindAttribute("position")->Value();
|
||||
const char* pos = curElem->FindAttribute("position")->Value();
|
||||
|
||||
/* create a camera device to be filled */
|
||||
CameraInfo *aCamera = new CameraInfo();
|
||||
CameraInfo* aCamera = new CameraInfo();
|
||||
|
||||
/* read camera device information */
|
||||
if (!readCameraDeviceInfo(aCamera, curElem)) {
|
||||
@@ -99,10 +99,7 @@ void ConfigManager::readCameraInfo(const XMLElement * const aCameraElem) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ConfigManager::readCameraDeviceInfo(CameraInfo *aCamera,
|
||||
const XMLElement *aDeviceElem) {
|
||||
bool ConfigManager::readCameraDeviceInfo(CameraInfo* aCamera, const XMLElement* aDeviceElem) {
|
||||
if (aCamera == nullptr || aDeviceElem == nullptr) {
|
||||
return false;
|
||||
}
|
||||
@@ -113,16 +110,11 @@ ConfigManager::readCameraDeviceInfo(CameraInfo *aCamera,
|
||||
|
||||
/* read device capabilities */
|
||||
totalEntries +=
|
||||
readCameraCapabilities(aDeviceElem->FirstChildElement("caps"),
|
||||
aCamera,
|
||||
totalDataSize);
|
||||
|
||||
readCameraCapabilities(aDeviceElem->FirstChildElement("caps"), aCamera, totalDataSize);
|
||||
|
||||
/* read camera metadata */
|
||||
totalEntries +=
|
||||
readCameraMetadata(aDeviceElem->FirstChildElement("characteristics"),
|
||||
aCamera,
|
||||
totalDataSize);
|
||||
totalEntries += readCameraMetadata(aDeviceElem->FirstChildElement("characteristics"), aCamera,
|
||||
totalDataSize);
|
||||
|
||||
/* construct camera_metadata_t */
|
||||
if (!constructCameraMetadata(aCamera, totalEntries, totalDataSize)) {
|
||||
@@ -133,40 +125,34 @@ ConfigManager::readCameraDeviceInfo(CameraInfo *aCamera,
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
ConfigManager::readCameraCapabilities(const XMLElement * const aCapElem,
|
||||
CameraInfo *aCamera,
|
||||
size_t &dataSize) {
|
||||
size_t ConfigManager::readCameraCapabilities(const XMLElement* const aCapElem, CameraInfo* aCamera,
|
||||
size_t& dataSize) {
|
||||
if (aCapElem == nullptr || aCamera == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
string token;
|
||||
const XMLElement *curElem = nullptr;
|
||||
const XMLElement* curElem = nullptr;
|
||||
|
||||
/* a list of supported camera parameters/controls */
|
||||
curElem = aCapElem->FirstChildElement("supported_controls");
|
||||
if (curElem != nullptr) {
|
||||
const XMLElement *ctrlElem = curElem->FirstChildElement("control");
|
||||
const XMLElement* ctrlElem = curElem->FirstChildElement("control");
|
||||
while (ctrlElem != nullptr) {
|
||||
const char *nameAttr = ctrlElem->FindAttribute("name")->Value();;
|
||||
const char* nameAttr = ctrlElem->FindAttribute("name")->Value();
|
||||
;
|
||||
const int32_t minVal = stoi(ctrlElem->FindAttribute("min")->Value());
|
||||
const int32_t maxVal = stoi(ctrlElem->FindAttribute("max")->Value());
|
||||
|
||||
int32_t stepVal = 1;
|
||||
const XMLAttribute *stepAttr = ctrlElem->FindAttribute("step");
|
||||
const XMLAttribute* stepAttr = ctrlElem->FindAttribute("step");
|
||||
if (stepAttr != nullptr) {
|
||||
stepVal = stoi(stepAttr->Value());
|
||||
}
|
||||
|
||||
CameraParam aParam;
|
||||
if (ConfigManagerUtil::convertToEvsCameraParam(nameAttr,
|
||||
aParam)) {
|
||||
aCamera->controls.emplace(
|
||||
aParam,
|
||||
make_tuple(minVal, maxVal, stepVal)
|
||||
);
|
||||
if (ConfigManagerUtil::convertToEvsCameraParam(nameAttr, aParam)) {
|
||||
aCamera->controls.emplace(aParam, make_tuple(minVal, maxVal, stepVal));
|
||||
}
|
||||
|
||||
ctrlElem = ctrlElem->NextSiblingElement("control");
|
||||
@@ -177,11 +163,11 @@ ConfigManager::readCameraCapabilities(const XMLElement * const aCapElem,
|
||||
curElem = aCapElem->FirstChildElement("stream");
|
||||
while (curElem != nullptr) {
|
||||
/* read 5 attributes */
|
||||
const XMLAttribute *idAttr = curElem->FindAttribute("id");
|
||||
const XMLAttribute *widthAttr = curElem->FindAttribute("width");
|
||||
const XMLAttribute *heightAttr = curElem->FindAttribute("height");
|
||||
const XMLAttribute *fmtAttr = curElem->FindAttribute("format");
|
||||
const XMLAttribute *fpsAttr = curElem->FindAttribute("framerate");
|
||||
const XMLAttribute* idAttr = curElem->FindAttribute("id");
|
||||
const XMLAttribute* widthAttr = curElem->FindAttribute("width");
|
||||
const XMLAttribute* heightAttr = curElem->FindAttribute("height");
|
||||
const XMLAttribute* fmtAttr = curElem->FindAttribute("format");
|
||||
const XMLAttribute* fpsAttr = curElem->FindAttribute("framerate");
|
||||
|
||||
const int32_t id = stoi(idAttr->Value());
|
||||
int32_t framerate = 0;
|
||||
@@ -190,16 +176,13 @@ ConfigManager::readCameraCapabilities(const XMLElement * const aCapElem,
|
||||
}
|
||||
|
||||
int32_t pixFormat;
|
||||
if (ConfigManagerUtil::convertToPixelFormat(fmtAttr->Value(),
|
||||
pixFormat)) {
|
||||
RawStreamConfiguration cfg = {
|
||||
id,
|
||||
stoi(widthAttr->Value()),
|
||||
stoi(heightAttr->Value()),
|
||||
pixFormat,
|
||||
ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
|
||||
framerate
|
||||
};
|
||||
if (ConfigManagerUtil::convertToPixelFormat(fmtAttr->Value(), pixFormat)) {
|
||||
RawStreamConfiguration cfg = {id,
|
||||
stoi(widthAttr->Value()),
|
||||
stoi(heightAttr->Value()),
|
||||
pixFormat,
|
||||
ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
|
||||
framerate};
|
||||
aCamera->streamConfigurations.insert_or_assign(id, cfg);
|
||||
}
|
||||
|
||||
@@ -207,70 +190,58 @@ ConfigManager::readCameraCapabilities(const XMLElement * const aCapElem,
|
||||
}
|
||||
|
||||
dataSize = calculate_camera_metadata_entry_data_size(
|
||||
get_camera_metadata_tag_type(
|
||||
ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS
|
||||
),
|
||||
aCamera->streamConfigurations.size() * kStreamCfgSz
|
||||
);
|
||||
get_camera_metadata_tag_type(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS),
|
||||
aCamera->streamConfigurations.size() * kStreamCfgSz);
|
||||
|
||||
/* a single camera metadata entry contains multiple stream configurations */
|
||||
return dataSize > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
ConfigManager::readCameraMetadata(const XMLElement * const aParamElem,
|
||||
CameraInfo *aCamera,
|
||||
size_t &dataSize) {
|
||||
size_t ConfigManager::readCameraMetadata(const XMLElement* const aParamElem, CameraInfo* aCamera,
|
||||
size_t& dataSize) {
|
||||
if (aParamElem == nullptr || aCamera == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const XMLElement *curElem = aParamElem->FirstChildElement("parameter");
|
||||
const XMLElement* curElem = aParamElem->FirstChildElement("parameter");
|
||||
size_t numEntries = 0;
|
||||
camera_metadata_tag_t tag;
|
||||
while (curElem != nullptr) {
|
||||
if (!ConfigManagerUtil::convertToMetadataTag(curElem->FindAttribute("name")->Value(),
|
||||
tag)) {
|
||||
switch(tag) {
|
||||
switch (tag) {
|
||||
case ANDROID_LENS_DISTORTION:
|
||||
case ANDROID_LENS_POSE_ROTATION:
|
||||
case ANDROID_LENS_POSE_TRANSLATION:
|
||||
case ANDROID_LENS_INTRINSIC_CALIBRATION: {
|
||||
/* float[] */
|
||||
size_t count = 0;
|
||||
void *data = ConfigManagerUtil::convertFloatArray(
|
||||
curElem->FindAttribute("size")->Value(),
|
||||
curElem->FindAttribute("value")->Value(),
|
||||
count
|
||||
);
|
||||
void* data = ConfigManagerUtil::convertFloatArray(
|
||||
curElem->FindAttribute("size")->Value(),
|
||||
curElem->FindAttribute("value")->Value(), count);
|
||||
|
||||
aCamera->cameraMetadata.insert_or_assign(
|
||||
tag, make_pair(make_unique<void *>(data), count)
|
||||
);
|
||||
tag, make_pair(make_unique<void*>(data), count));
|
||||
|
||||
++numEntries;
|
||||
dataSize += calculate_camera_metadata_entry_data_size(
|
||||
get_camera_metadata_tag_type(tag), count
|
||||
);
|
||||
get_camera_metadata_tag_type(tag), count);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ANDROID_REQUEST_AVAILABLE_CAPABILITIES: {
|
||||
camera_metadata_enum_android_request_available_capabilities_t *data =
|
||||
new camera_metadata_enum_android_request_available_capabilities_t[1];
|
||||
camera_metadata_enum_android_request_available_capabilities_t* data =
|
||||
new camera_metadata_enum_android_request_available_capabilities_t[1];
|
||||
if (ConfigManagerUtil::convertToCameraCapability(
|
||||
curElem->FindAttribute("value")->Value(), *data)) {
|
||||
curElem->FindAttribute("value")->Value(),
|
||||
aCamera->cameraMetadata.insert_or_assign(
|
||||
tag, make_pair(make_unique<void *>(data), 1)
|
||||
);
|
||||
curElem->FindAttribute("value")->Value(), *data)) {
|
||||
curElem->FindAttribute("value")->Value(),
|
||||
aCamera->cameraMetadata.insert_or_assign(
|
||||
tag, make_pair(make_unique<void*>(data), 1));
|
||||
|
||||
++numEntries;
|
||||
dataSize += calculate_camera_metadata_entry_data_size(
|
||||
get_camera_metadata_tag_type(tag), 1
|
||||
);
|
||||
get_camera_metadata_tag_type(tag), 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -278,13 +249,11 @@ ConfigManager::readCameraMetadata(const XMLElement * const aParamElem,
|
||||
case ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS: {
|
||||
/* a comma-separated list of physical camera devices */
|
||||
size_t len = strlen(curElem->FindAttribute("value")->Value());
|
||||
char *data = new char[len + 1];
|
||||
memcpy(data,
|
||||
curElem->FindAttribute("value")->Value(),
|
||||
len * sizeof(char));
|
||||
char* data = new char[len + 1];
|
||||
memcpy(data, curElem->FindAttribute("value")->Value(), len * sizeof(char));
|
||||
|
||||
/* replace commas with null char */
|
||||
char *p = data;
|
||||
char* p = data;
|
||||
while (*p != '\0') {
|
||||
if (*p == ',') {
|
||||
*p = '\0';
|
||||
@@ -293,19 +262,16 @@ ConfigManager::readCameraMetadata(const XMLElement * const aParamElem,
|
||||
}
|
||||
|
||||
aCamera->cameraMetadata.insert_or_assign(
|
||||
tag, make_pair(make_unique<void *>(data), len)
|
||||
);
|
||||
tag, make_pair(make_unique<void*>(data), len));
|
||||
|
||||
++numEntries;
|
||||
dataSize += calculate_camera_metadata_entry_data_size(
|
||||
get_camera_metadata_tag_type(tag), len
|
||||
);
|
||||
get_camera_metadata_tag_type(tag), len);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ALOGW("Parameter %s is not supported",
|
||||
curElem->FindAttribute("name")->Value());
|
||||
ALOGW("Parameter %s is not supported", curElem->FindAttribute("name")->Value());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -316,11 +282,8 @@ ConfigManager::readCameraMetadata(const XMLElement * const aParamElem,
|
||||
return numEntries;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ConfigManager::constructCameraMetadata(CameraInfo *aCamera,
|
||||
const size_t totalEntries,
|
||||
const size_t totalDataSize) {
|
||||
bool ConfigManager::constructCameraMetadata(CameraInfo* aCamera, const size_t totalEntries,
|
||||
const size_t totalDataSize) {
|
||||
if (aCamera == nullptr || !aCamera->allocate(totalEntries, totalDataSize)) {
|
||||
ALOGE("Failed to allocate memory for camera metadata");
|
||||
return false;
|
||||
@@ -328,16 +291,15 @@ ConfigManager::constructCameraMetadata(CameraInfo *aCamera,
|
||||
|
||||
const size_t numStreamConfigs = aCamera->streamConfigurations.size();
|
||||
unique_ptr<int32_t[]> data(new int32_t[kStreamCfgSz * numStreamConfigs]);
|
||||
int32_t *ptr = data.get();
|
||||
for (auto &cfg : aCamera->streamConfigurations) {
|
||||
int32_t* ptr = data.get();
|
||||
for (auto& cfg : aCamera->streamConfigurations) {
|
||||
for (auto i = 0; i < kStreamCfgSz; ++i) {
|
||||
*ptr++ = cfg.second[i];
|
||||
*ptr++ = cfg.second[i];
|
||||
}
|
||||
}
|
||||
int32_t err = add_camera_metadata_entry(aCamera->characteristics,
|
||||
ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
|
||||
data.get(),
|
||||
numStreamConfigs * kStreamCfgSz);
|
||||
data.get(), numStreamConfigs * kStreamCfgSz);
|
||||
|
||||
if (err) {
|
||||
ALOGE("Failed to add stream configurations to metadata, ignored");
|
||||
@@ -345,11 +307,9 @@ ConfigManager::constructCameraMetadata(CameraInfo *aCamera,
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
for (auto &[tag, entry] : aCamera->cameraMetadata) {
|
||||
for (auto& [tag, entry] : aCamera->cameraMetadata) {
|
||||
/* try to add new camera metadata entry */
|
||||
int32_t err = add_camera_metadata_entry(aCamera->characteristics,
|
||||
tag,
|
||||
entry.first.get(),
|
||||
int32_t err = add_camera_metadata_entry(aCamera->characteristics, tag, entry.first.get(),
|
||||
entry.second);
|
||||
if (err) {
|
||||
ALOGE("Failed to add an entry with a tag 0x%X", tag);
|
||||
@@ -376,8 +336,7 @@ ConfigManager::constructCameraMetadata(CameraInfo *aCamera,
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
void ConfigManager::readSystemInfo(const XMLElement * const aSysElem) {
|
||||
void ConfigManager::readSystemInfo(const XMLElement* const aSysElem) {
|
||||
if (aSysElem == nullptr) {
|
||||
return;
|
||||
}
|
||||
@@ -389,24 +348,22 @@ void ConfigManager::readSystemInfo(const XMLElement * const aSysElem) {
|
||||
*/
|
||||
|
||||
/* read number of cameras available in the system */
|
||||
const XMLElement *xmlElem = aSysElem->FirstChildElement("num_cameras");
|
||||
const XMLElement* xmlElem = aSysElem->FirstChildElement("num_cameras");
|
||||
if (xmlElem != nullptr) {
|
||||
mSystemInfo.numCameras =
|
||||
stoi(xmlElem->FindAttribute("value")->Value());
|
||||
mSystemInfo.numCameras = stoi(xmlElem->FindAttribute("value")->Value());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ConfigManager::readDisplayInfo(const XMLElement * const aDisplayElem) {
|
||||
void ConfigManager::readDisplayInfo(const XMLElement* const aDisplayElem) {
|
||||
if (aDisplayElem == nullptr) {
|
||||
ALOGW("XML file does not have required camera element");
|
||||
return;
|
||||
}
|
||||
|
||||
const XMLElement *curDev = aDisplayElem->FirstChildElement("device");
|
||||
const XMLElement* curDev = aDisplayElem->FirstChildElement("device");
|
||||
while (curDev != nullptr) {
|
||||
const char *id = curDev->FindAttribute("id")->Value();
|
||||
//const char *pos = curDev->FirstAttribute("position")->Value();
|
||||
const char* id = curDev->FindAttribute("id")->Value();
|
||||
// const char *pos = curDev->FirstAttribute("position")->Value();
|
||||
|
||||
unique_ptr<DisplayInfo> dpy(new DisplayInfo());
|
||||
if (dpy == nullptr) {
|
||||
@@ -414,27 +371,26 @@ void ConfigManager::readDisplayInfo(const XMLElement * const aDisplayElem) {
|
||||
return;
|
||||
}
|
||||
|
||||
const XMLElement *cap = curDev->FirstChildElement("caps");
|
||||
const XMLElement* cap = curDev->FirstChildElement("caps");
|
||||
if (cap != nullptr) {
|
||||
const XMLElement *curStream = cap->FirstChildElement("stream");
|
||||
const XMLElement* curStream = cap->FirstChildElement("stream");
|
||||
while (curStream != nullptr) {
|
||||
/* read 4 attributes */
|
||||
const XMLAttribute *idAttr = curStream->FindAttribute("id");
|
||||
const XMLAttribute *widthAttr = curStream->FindAttribute("width");
|
||||
const XMLAttribute *heightAttr = curStream->FindAttribute("height");
|
||||
const XMLAttribute *fmtAttr = curStream->FindAttribute("format");
|
||||
const XMLAttribute* idAttr = curStream->FindAttribute("id");
|
||||
const XMLAttribute* widthAttr = curStream->FindAttribute("width");
|
||||
const XMLAttribute* heightAttr = curStream->FindAttribute("height");
|
||||
const XMLAttribute* fmtAttr = curStream->FindAttribute("format");
|
||||
|
||||
const int32_t id = stoi(idAttr->Value());
|
||||
int32_t pixFormat;
|
||||
if (ConfigManagerUtil::convertToPixelFormat(fmtAttr->Value(),
|
||||
pixFormat)) {
|
||||
if (ConfigManagerUtil::convertToPixelFormat(fmtAttr->Value(), pixFormat)) {
|
||||
RawStreamConfiguration cfg = {
|
||||
id,
|
||||
stoi(widthAttr->Value()),
|
||||
stoi(heightAttr->Value()),
|
||||
pixFormat,
|
||||
ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT,
|
||||
0 // unused
|
||||
id,
|
||||
stoi(widthAttr->Value()),
|
||||
stoi(heightAttr->Value()),
|
||||
pixFormat,
|
||||
ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT,
|
||||
0 // unused
|
||||
};
|
||||
dpy->streamConfigurations.insert_or_assign(id, cfg);
|
||||
}
|
||||
@@ -450,7 +406,6 @@ void ConfigManager::readDisplayInfo(const XMLElement * const aDisplayElem) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
bool ConfigManager::readConfigDataFromXML() noexcept {
|
||||
XMLDocument xmlDoc;
|
||||
|
||||
@@ -464,7 +419,7 @@ bool ConfigManager::readConfigDataFromXML() noexcept {
|
||||
}
|
||||
|
||||
/* retrieve the root element */
|
||||
const XMLElement *rootElem = xmlDoc.RootElement();
|
||||
const XMLElement* rootElem = xmlDoc.RootElement();
|
||||
if (strcmp(rootElem->Name(), "configuration")) {
|
||||
ALOGE("A configuration file is not in the required format. "
|
||||
"See /etc/automotive/evs/evs_configuration.dtd");
|
||||
@@ -487,12 +442,10 @@ bool ConfigManager::readConfigDataFromXML() noexcept {
|
||||
ALOGI("Parsing configuration file takes %lf (ms)",
|
||||
(double)(parsingEnd - parsingStart) / 1000000.0);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<ConfigManager> ConfigManager::Create(const char *path) {
|
||||
std::unique_ptr<ConfigManager> ConfigManager::Create(const char* path) {
|
||||
unique_ptr<ConfigManager> cfgMgr(new ConfigManager(path));
|
||||
|
||||
/*
|
||||
@@ -510,3 +463,4 @@ std::unique_ptr<ConfigManager> ConfigManager::Create(const char *path) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android::hardware::automotive::evs::V1_1::implementation
|
||||
|
||||
@@ -13,28 +13,26 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef CONFIG_MANAGER_H
|
||||
#define CONFIG_MANAGER_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <tinyxml2.h>
|
||||
|
||||
#include <system/camera_metadata.h>
|
||||
#include <log/log.h>
|
||||
#include <android/hardware/automotive/evs/1.1/types.h>
|
||||
#ifndef ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_CONFIGMANAGER_H
|
||||
#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_CONFIGMANAGER_H
|
||||
|
||||
#include "ConfigManagerUtil.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace tinyxml2;
|
||||
#include <android/hardware/automotive/evs/1.1/types.h>
|
||||
#include <log/log.h>
|
||||
#include <system/camera_metadata.h>
|
||||
#include <tinyxml2.h>
|
||||
|
||||
using ::android::hardware::hidl_vec;
|
||||
using ::android::hardware::camera::device::V3_2::Stream;
|
||||
using ::android::hardware::automotive::evs::V1_1::CameraParam;
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace android::hardware::automotive::evs::V1_1::implementation {
|
||||
|
||||
using hardware::hidl_vec;
|
||||
using hardware::automotive::evs::V1_1::CameraParam;
|
||||
using hardware::camera::device::V3_2::Stream;
|
||||
|
||||
/*
|
||||
* Plese note that this is different from what is defined in
|
||||
@@ -45,8 +43,8 @@ const size_t kStreamCfgSz = 6;
|
||||
typedef std::array<int32_t, kStreamCfgSz> RawStreamConfiguration;
|
||||
|
||||
class ConfigManager {
|
||||
public:
|
||||
static std::unique_ptr<ConfigManager> Create(const char *path = "");
|
||||
public:
|
||||
static std::unique_ptr<ConfigManager> Create(const char* path = "");
|
||||
ConfigManager(const ConfigManager&) = delete;
|
||||
ConfigManager& operator=(const ConfigManager&) = delete;
|
||||
|
||||
@@ -54,15 +52,11 @@ public:
|
||||
|
||||
/* Camera device's capabilities and metadata */
|
||||
class CameraInfo {
|
||||
public:
|
||||
CameraInfo() :
|
||||
characteristics(nullptr) {
|
||||
/* Nothing to do */
|
||||
public:
|
||||
CameraInfo() : characteristics(nullptr) { /* Nothing to do */
|
||||
}
|
||||
|
||||
virtual ~CameraInfo() {
|
||||
free_camera_metadata(characteristics);
|
||||
}
|
||||
virtual ~CameraInfo() { free_camera_metadata(characteristics); }
|
||||
|
||||
/* Allocate memory for camera_metadata_t */
|
||||
bool allocate(size_t entry_cap, size_t data_cap) {
|
||||
@@ -79,50 +73,49 @@ public:
|
||||
* List of supported controls that the primary client can program.
|
||||
* Paraemters are stored with its valid range
|
||||
*/
|
||||
unordered_map<CameraParam,
|
||||
tuple<int32_t, int32_t, int32_t>> controls;
|
||||
std::unordered_map<CameraParam, std::tuple<int32_t, int32_t, int32_t>> controls;
|
||||
|
||||
/*
|
||||
* List of supported output stream configurations; each array stores
|
||||
* format, width, height, and direction values in the order.
|
||||
*/
|
||||
unordered_map<int32_t, RawStreamConfiguration> streamConfigurations;
|
||||
std::unordered_map<int32_t, RawStreamConfiguration> streamConfigurations;
|
||||
|
||||
/*
|
||||
* Internal storage for camera metadata. Each entry holds a pointer to
|
||||
* data and number of elements
|
||||
*/
|
||||
unordered_map<camera_metadata_tag_t,
|
||||
pair<unique_ptr<void *>, size_t>> cameraMetadata;
|
||||
std::unordered_map<camera_metadata_tag_t, std::pair<std::unique_ptr<void*>, size_t>>
|
||||
cameraMetadata;
|
||||
|
||||
/* Camera module characteristics */
|
||||
camera_metadata_t *characteristics;
|
||||
camera_metadata_t* characteristics;
|
||||
};
|
||||
|
||||
class CameraGroupInfo : public CameraInfo {
|
||||
public:
|
||||
public:
|
||||
CameraGroupInfo() {}
|
||||
|
||||
/* ID of member camera devices */
|
||||
unordered_set<string> devices;
|
||||
std::unordered_set<std::string> devices;
|
||||
|
||||
/* The capture operation of member camera devices are synchronized */
|
||||
bool synchronized = false;
|
||||
};
|
||||
|
||||
class SystemInfo {
|
||||
public:
|
||||
public:
|
||||
/* number of available cameras */
|
||||
int32_t numCameras = 0;
|
||||
};
|
||||
|
||||
class DisplayInfo {
|
||||
public:
|
||||
public:
|
||||
/*
|
||||
* List of supported input stream configurations; each array stores
|
||||
* format, width, height, and direction values in the order.
|
||||
*/
|
||||
unordered_map<int32_t, RawStreamConfiguration> streamConfigurations;
|
||||
std::unordered_map<int32_t, RawStreamConfiguration> streamConfigurations;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -131,80 +124,74 @@ public:
|
||||
* @return SystemInfo
|
||||
* Constant reference of SystemInfo.
|
||||
*/
|
||||
const SystemInfo &getSystemInfo() {
|
||||
return mSystemInfo;
|
||||
}
|
||||
const SystemInfo& getSystemInfo() { return mSystemInfo; }
|
||||
|
||||
/*
|
||||
* Return a list of cameras
|
||||
*
|
||||
* This function assumes that it is not being called frequently.
|
||||
*
|
||||
* @return vector<string>
|
||||
* @return std::vector<std::string>
|
||||
* A vector that contains unique camera device identifiers.
|
||||
*/
|
||||
vector<string> getCameraList() {
|
||||
vector<string> aList;
|
||||
for (auto &v : mCameraInfo) {
|
||||
aList.emplace_back(v.first);
|
||||
std::vector<std::string> getCameraList() {
|
||||
std::vector<std::string> aList;
|
||||
for (auto& v : mCameraInfo) {
|
||||
aList.push_back(v.first);
|
||||
}
|
||||
|
||||
return aList;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return a list of cameras
|
||||
*
|
||||
* @return CameraGroupInfo
|
||||
* A pointer to a camera group identified by a given id.
|
||||
*/
|
||||
unique_ptr<CameraGroupInfo>& getCameraGroupInfo(const string& gid) {
|
||||
std::unique_ptr<CameraGroupInfo>& getCameraGroupInfo(const std::string& gid) {
|
||||
return mCameraGroupInfos[gid];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return a camera metadata
|
||||
*
|
||||
* @param cameraId
|
||||
* Unique camera node identifier in string
|
||||
* Unique camera node identifier in std::string
|
||||
*
|
||||
* @return unique_ptr<CameraInfo>
|
||||
* @return std::unique_ptr<CameraInfo>
|
||||
* A pointer to CameraInfo that is associated with a given camera
|
||||
* ID. This returns a null pointer if this does not recognize a
|
||||
* given camera identifier.
|
||||
*/
|
||||
unique_ptr<CameraInfo>& getCameraInfo(const string cameraId) noexcept {
|
||||
std::unique_ptr<CameraInfo>& getCameraInfo(const std::string cameraId) noexcept {
|
||||
return mCameraInfo[cameraId];
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
/* Constructors */
|
||||
ConfigManager(const char *xmlPath) :
|
||||
mConfigFilePath(xmlPath) {
|
||||
}
|
||||
ConfigManager(const char* xmlPath) : mConfigFilePath(xmlPath) {}
|
||||
|
||||
/* System configuration */
|
||||
SystemInfo mSystemInfo;
|
||||
|
||||
/* Internal data structure for camera device information */
|
||||
unordered_map<string, unique_ptr<CameraInfo>> mCameraInfo;
|
||||
std::unordered_map<std::string, std::unique_ptr<CameraInfo>> mCameraInfo;
|
||||
|
||||
/* Internal data structure for camera device information */
|
||||
unordered_map<string, unique_ptr<DisplayInfo>> mDisplayInfo;
|
||||
std::unordered_map<std::string, std::unique_ptr<DisplayInfo>> mDisplayInfo;
|
||||
|
||||
/* Camera groups are stored in <groud id, CameraGroupInfo> hash map */
|
||||
unordered_map<string, unique_ptr<CameraGroupInfo>> mCameraGroupInfos;
|
||||
std::unordered_map<std::string, std::unique_ptr<CameraGroupInfo>> mCameraGroupInfos;
|
||||
|
||||
/*
|
||||
* Camera positions are stored in <position, camera id set> hash map.
|
||||
* The position must be one of front, rear, left, and right.
|
||||
*/
|
||||
unordered_map<string, unordered_set<string>> mCameraPosition;
|
||||
std::unordered_map<std::string, std::unordered_set<std::string>> mCameraPosition;
|
||||
|
||||
/* A path to XML configuration file */
|
||||
const char *mConfigFilePath;
|
||||
const char* mConfigFilePath;
|
||||
|
||||
/*
|
||||
* Parse a given EVS configuration file and store the information
|
||||
@@ -221,7 +208,7 @@ private:
|
||||
* @param aSysElem
|
||||
* A pointer to "system" XML element.
|
||||
*/
|
||||
void readSystemInfo(const XMLElement * const aSysElem);
|
||||
void readSystemInfo(const tinyxml2::XMLElement* const aSysElem);
|
||||
|
||||
/*
|
||||
* read the information of camera devices
|
||||
@@ -230,7 +217,7 @@ private:
|
||||
* A pointer to "camera" XML element that may contain multiple
|
||||
* "device" elements.
|
||||
*/
|
||||
void readCameraInfo(const XMLElement * const aCameraElem);
|
||||
void readCameraInfo(const tinyxml2::XMLElement* const aCameraElem);
|
||||
|
||||
/*
|
||||
* read display device information
|
||||
@@ -239,7 +226,7 @@ private:
|
||||
* A pointer to "display" XML element that may contain multiple
|
||||
* "device" elements.
|
||||
*/
|
||||
void readDisplayInfo(const XMLElement * const aDisplayElem);
|
||||
void readDisplayInfo(const tinyxml2::XMLElement* const aDisplayElem);
|
||||
|
||||
/*
|
||||
* read camera device information
|
||||
@@ -255,8 +242,7 @@ private:
|
||||
* Return false upon any failure in reading and processing camera
|
||||
* device information.
|
||||
*/
|
||||
bool readCameraDeviceInfo(CameraInfo *aCamera,
|
||||
const XMLElement *aDeviceElem);
|
||||
bool readCameraDeviceInfo(CameraInfo* aCamera, const tinyxml2::XMLElement* aDeviceElem);
|
||||
|
||||
/*
|
||||
* read camera metadata
|
||||
@@ -273,9 +259,8 @@ private:
|
||||
* @return size_t
|
||||
* Number of camera metadata entries
|
||||
*/
|
||||
size_t readCameraCapabilities(const XMLElement * const aCapElem,
|
||||
CameraInfo *aCamera,
|
||||
size_t &dataSize);
|
||||
size_t readCameraCapabilities(const tinyxml2::XMLElement* const aCapElem, CameraInfo* aCamera,
|
||||
size_t& dataSize);
|
||||
|
||||
/*
|
||||
* read camera metadata
|
||||
@@ -291,9 +276,8 @@ private:
|
||||
* @return size_t
|
||||
* Number of camera metadata entries
|
||||
*/
|
||||
size_t readCameraMetadata(const XMLElement * const aParamElem,
|
||||
CameraInfo *aCamera,
|
||||
size_t &dataSize);
|
||||
size_t readCameraMetadata(const tinyxml2::XMLElement* const aParamElem, CameraInfo* aCamera,
|
||||
size_t& dataSize);
|
||||
|
||||
/*
|
||||
* construct camera_metadata_t from camera capabilities and metadata
|
||||
@@ -310,9 +294,9 @@ private:
|
||||
* or its size is not large enough to add all found camera metadata
|
||||
* entries.
|
||||
*/
|
||||
bool constructCameraMetadata(CameraInfo *aCamera,
|
||||
const size_t totalEntries,
|
||||
bool constructCameraMetadata(CameraInfo* aCamera, const size_t totalEntries,
|
||||
const size_t totalDataSize);
|
||||
};
|
||||
#endif // CONFIG_MANAGER_H
|
||||
|
||||
} // namespace android::hardware::automotive::evs::V1_1::implementation
|
||||
#endif // ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_CONFIGMANAGER_H
|
||||
|
||||
@@ -16,43 +16,45 @@
|
||||
|
||||
#include "ConfigManagerUtil.h"
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include <log/log.h>
|
||||
#include <system/graphics-base-v1.0.h>
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
bool ConfigManagerUtil::convertToEvsCameraParam(const string &id,
|
||||
CameraParam &camParam) {
|
||||
namespace android::hardware::automotive::evs::V1_1::implementation {
|
||||
|
||||
using namespace std;
|
||||
|
||||
bool ConfigManagerUtil::convertToEvsCameraParam(const string& id, CameraParam& camParam) {
|
||||
string trimmed = ConfigManagerUtil::trimString(id);
|
||||
bool success = true;
|
||||
|
||||
if (!trimmed.compare("BRIGHTNESS")) {
|
||||
camParam = CameraParam::BRIGHTNESS;
|
||||
camParam = CameraParam::BRIGHTNESS;
|
||||
} else if (!trimmed.compare("CONTRAST")) {
|
||||
camParam = CameraParam::CONTRAST;
|
||||
camParam = CameraParam::CONTRAST;
|
||||
} else if (!trimmed.compare("AUTOGAIN")) {
|
||||
camParam = CameraParam::AUTOGAIN;
|
||||
camParam = CameraParam::AUTOGAIN;
|
||||
} else if (!trimmed.compare("GAIN")) {
|
||||
camParam = CameraParam::GAIN;
|
||||
camParam = CameraParam::GAIN;
|
||||
} else if (!trimmed.compare("AUTO_WHITE_BALANCE")) {
|
||||
camParam = CameraParam::AUTO_WHITE_BALANCE;
|
||||
camParam = CameraParam::AUTO_WHITE_BALANCE;
|
||||
} else if (!trimmed.compare("WHITE_BALANCE_TEMPERATURE")) {
|
||||
camParam = CameraParam::WHITE_BALANCE_TEMPERATURE;
|
||||
camParam = CameraParam::WHITE_BALANCE_TEMPERATURE;
|
||||
} else if (!trimmed.compare("SHARPNESS")) {
|
||||
camParam = CameraParam::SHARPNESS;
|
||||
camParam = CameraParam::SHARPNESS;
|
||||
} else if (!trimmed.compare("AUTO_EXPOSURE")) {
|
||||
camParam = CameraParam::AUTO_EXPOSURE;
|
||||
camParam = CameraParam::AUTO_EXPOSURE;
|
||||
} else if (!trimmed.compare("ABSOLUTE_EXPOSURE")) {
|
||||
camParam = CameraParam::ABSOLUTE_EXPOSURE;
|
||||
camParam = CameraParam::ABSOLUTE_EXPOSURE;
|
||||
} else if (!trimmed.compare("ABSOLUTE_FOCUS")) {
|
||||
camParam = CameraParam::ABSOLUTE_FOCUS;
|
||||
camParam = CameraParam::ABSOLUTE_FOCUS;
|
||||
} else if (!trimmed.compare("AUTO_FOCUS")) {
|
||||
camParam = CameraParam::AUTO_FOCUS;
|
||||
camParam = CameraParam::AUTO_FOCUS;
|
||||
} else if (!trimmed.compare("ABSOLUTE_ZOOM")) {
|
||||
camParam = CameraParam::ABSOLUTE_ZOOM;
|
||||
camParam = CameraParam::ABSOLUTE_ZOOM;
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
@@ -60,18 +62,16 @@ bool ConfigManagerUtil::convertToEvsCameraParam(const string &id,
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
bool ConfigManagerUtil::convertToPixelFormat(const string &format,
|
||||
int32_t &pixFormat) {
|
||||
bool ConfigManagerUtil::convertToPixelFormat(const string& format, int32_t& pixFormat) {
|
||||
string trimmed = ConfigManagerUtil::trimString(format);
|
||||
bool success = true;
|
||||
|
||||
if (!trimmed.compare("RGBA_8888")) {
|
||||
pixFormat = HAL_PIXEL_FORMAT_RGBA_8888;
|
||||
pixFormat = HAL_PIXEL_FORMAT_RGBA_8888;
|
||||
} else if (!trimmed.compare("YCRCB_420_SP")) {
|
||||
pixFormat = HAL_PIXEL_FORMAT_YCRCB_420_SP;
|
||||
pixFormat = HAL_PIXEL_FORMAT_YCRCB_420_SP;
|
||||
} else if (!trimmed.compare("YCBCR_422_I")) {
|
||||
pixFormat = HAL_PIXEL_FORMAT_YCBCR_422_I;
|
||||
pixFormat = HAL_PIXEL_FORMAT_YCBCR_422_I;
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
@@ -79,21 +79,19 @@ bool ConfigManagerUtil::convertToPixelFormat(const string &format,
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
bool ConfigManagerUtil::convertToMetadataTag(const char *name,
|
||||
camera_metadata_tag &aTag) {
|
||||
bool ConfigManagerUtil::convertToMetadataTag(const char* name, camera_metadata_tag& aTag) {
|
||||
if (!strcmp(name, "LENS_DISTORTION")) {
|
||||
aTag = ANDROID_LENS_DISTORTION;
|
||||
aTag = ANDROID_LENS_DISTORTION;
|
||||
} else if (!strcmp(name, "LENS_INTRINSIC_CALIBRATION")) {
|
||||
aTag = ANDROID_LENS_INTRINSIC_CALIBRATION;
|
||||
aTag = ANDROID_LENS_INTRINSIC_CALIBRATION;
|
||||
} else if (!strcmp(name, "LENS_POSE_ROTATION")) {
|
||||
aTag = ANDROID_LENS_POSE_ROTATION;
|
||||
aTag = ANDROID_LENS_POSE_ROTATION;
|
||||
} else if (!strcmp(name, "LENS_POSE_TRANSLATION")) {
|
||||
aTag = ANDROID_LENS_POSE_TRANSLATION;
|
||||
aTag = ANDROID_LENS_POSE_TRANSLATION;
|
||||
} else if (!strcmp(name, "REQUEST_AVAILABLE_CAPABILITIES")) {
|
||||
aTag = ANDROID_REQUEST_AVAILABLE_CAPABILITIES;
|
||||
aTag = ANDROID_REQUEST_AVAILABLE_CAPABILITIES;
|
||||
} else if (!strcmp(name, "LOGICAL_MULTI_CAMERA_PHYSICAL_IDS")) {
|
||||
aTag = ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS;
|
||||
aTag = ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -101,11 +99,8 @@ bool ConfigManagerUtil::convertToMetadataTag(const char *name,
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ConfigManagerUtil::convertToCameraCapability(
|
||||
const char *name,
|
||||
camera_metadata_enum_android_request_available_capabilities_t &cap) {
|
||||
|
||||
const char* name, camera_metadata_enum_android_request_available_capabilities_t& cap) {
|
||||
if (!strcmp(name, "DEPTH_OUTPUT")) {
|
||||
cap = ANDROID_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT;
|
||||
} else if (!strcmp(name, "LOGICAL_MULTI_CAMERA")) {
|
||||
@@ -121,14 +116,13 @@ bool ConfigManagerUtil::convertToCameraCapability(
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
float *ConfigManagerUtil::convertFloatArray(const char *sz, const char *vals,
|
||||
size_t &count, const char delimiter) {
|
||||
float* ConfigManagerUtil::convertFloatArray(const char* sz, const char* vals, size_t& count,
|
||||
const char delimiter) {
|
||||
string size_string(sz);
|
||||
string value_string(vals);
|
||||
|
||||
count = stoi(size_string);
|
||||
float *result = new float[count];
|
||||
float* result = new float[count];
|
||||
stringstream values(value_string);
|
||||
|
||||
int32_t idx = 0;
|
||||
@@ -140,8 +134,7 @@ float *ConfigManagerUtil::convertFloatArray(const char *sz, const char *vals,
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
string ConfigManagerUtil::trimString(const string &src, const string &ws) {
|
||||
string ConfigManagerUtil::trimString(const string& src, const string& ws) {
|
||||
const auto s = src.find_first_not_of(ws);
|
||||
if (s == string::npos) {
|
||||
return "";
|
||||
@@ -153,3 +146,4 @@ string ConfigManagerUtil::trimString(const string &src, const string &ws) {
|
||||
return src.substr(s, r);
|
||||
}
|
||||
|
||||
} // namespace android::hardware::automotive::evs::V1_1::implementation
|
||||
|
||||
@@ -13,57 +13,48 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef CONFIG_MANAGER_UTIL_H
|
||||
#define CONFIG_MANAGER_UTIL_H
|
||||
#ifndef ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_CONFIGMANAGERUTIL_H
|
||||
#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_CONFIGMANAGERUTIL_H
|
||||
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <system/camera_metadata.h>
|
||||
#include <android/hardware/automotive/evs/1.1/types.h>
|
||||
#include <system/camera_metadata.h>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
using namespace std;
|
||||
using ::android::hardware::automotive::evs::V1_1::CameraParam;
|
||||
|
||||
namespace android::hardware::automotive::evs::V1_1::implementation {
|
||||
|
||||
class ConfigManagerUtil {
|
||||
public:
|
||||
public:
|
||||
/**
|
||||
* Convert a given string into V4L2_CID_*
|
||||
*/
|
||||
static bool convertToEvsCameraParam(const string &id,
|
||||
CameraParam &camParam);
|
||||
static bool convertToEvsCameraParam(const std::string& id, CameraParam& camParam);
|
||||
/**
|
||||
* Convert a given string into android.hardware.graphics.common.PixelFormat
|
||||
*/
|
||||
static bool convertToPixelFormat(const string &format,
|
||||
int32_t &pixelFormat);
|
||||
static bool convertToPixelFormat(const std::string& format, int32_t& pixelFormat);
|
||||
/**
|
||||
* Convert a given string into corresponding camera metadata data tag defined in
|
||||
* system/media/camera/include/system/camera_metadta_tags.h
|
||||
*/
|
||||
static bool convertToMetadataTag(const char *name,
|
||||
camera_metadata_tag &aTag);
|
||||
static bool convertToMetadataTag(const char* name, camera_metadata_tag& aTag);
|
||||
/**
|
||||
* Convert a given string into a floating value array
|
||||
*/
|
||||
static float *convertFloatArray(const char *sz,
|
||||
const char *vals,
|
||||
size_t &count,
|
||||
static float* convertFloatArray(const char* sz, const char* vals, size_t& count,
|
||||
const char delimiter = ',');
|
||||
/**
|
||||
* Trim a string
|
||||
*/
|
||||
static string trimString(const string &src,
|
||||
const string &ws = " \n\r\t\f\v");
|
||||
static std::string trimString(const std::string& src, const std::string& ws = " \n\r\t\f\v");
|
||||
|
||||
/**
|
||||
* Convert a given string to corresponding camera capabilities
|
||||
*/
|
||||
static bool convertToCameraCapability(
|
||||
const char *name,
|
||||
camera_metadata_enum_android_request_available_capabilities_t &cap);
|
||||
|
||||
const char* name, camera_metadata_enum_android_request_available_capabilities_t& cap);
|
||||
};
|
||||
|
||||
#endif // CONFIG_MANAGER_UTIL_H
|
||||
} // namespace android::hardware::automotive::evs::V1_1::implementation
|
||||
|
||||
#endif // ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_CONFIGMANAGERUTIL_H
|
||||
|
||||
@@ -14,69 +14,69 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "android.hardware.automotive.evs@1.1-service"
|
||||
|
||||
#include "EvsCamera.h"
|
||||
#include "ConfigManager.h"
|
||||
#include "EvsEnumerator.h"
|
||||
|
||||
#include <ui/GraphicBufferAllocator.h>
|
||||
#include <ui/GraphicBufferMapper.h>
|
||||
#include <utils/SystemClock.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace automotive {
|
||||
namespace evs {
|
||||
namespace V1_1 {
|
||||
namespace implementation {
|
||||
|
||||
|
||||
// Special camera names for which we'll initialize alternate test data
|
||||
const char EvsCamera::kCameraName_Backup[] = "backup";
|
||||
|
||||
namespace {
|
||||
|
||||
// Arbitrary limit on number of graphics buffers allowed to be allocated
|
||||
// Safeguards against unreasonable resource consumption and provides a testable limit
|
||||
const unsigned MAX_BUFFERS_IN_FLIGHT = 100;
|
||||
constexpr unsigned kMaxBuffersInFlight = 100;
|
||||
|
||||
// Minimum number of buffers to run a video stream
|
||||
constexpr int kMinimumBuffersInFlight = 1;
|
||||
|
||||
EvsCamera::EvsCamera(const char *id,
|
||||
unique_ptr<ConfigManager::CameraInfo> &camInfo) :
|
||||
mFramesAllowed(0),
|
||||
mFramesInUse(0),
|
||||
mStreamState(STOPPED),
|
||||
mCameraInfo(camInfo) {
|
||||
// Colors for the colorbar test pattern in ABGR format
|
||||
constexpr uint32_t kColors[] = {
|
||||
0xFFFFFFFF, // white
|
||||
0xFF00FFFF, // yellow
|
||||
0xFFFFFF00, // cyan
|
||||
0xFF00FF00, // green
|
||||
0xFFFF00FF, // fuchsia
|
||||
0xFF0000FF, // red
|
||||
0xFFFF0000, // blue
|
||||
0xFF000000, // black
|
||||
};
|
||||
constexpr uint32_t kNumColors = sizeof(kColors) / sizeof(kColors[0]);
|
||||
|
||||
ALOGD("EvsCamera instantiated");
|
||||
} // namespace
|
||||
|
||||
namespace android::hardware::automotive::evs::V1_1::implementation {
|
||||
|
||||
using V1_0::EvsResult;
|
||||
|
||||
EvsCamera::EvsCamera(const char* id, std::unique_ptr<ConfigManager::CameraInfo>& camInfo)
|
||||
: mFramesAllowed(0), mFramesInUse(0), mStreamState(STOPPED), mCameraInfo(camInfo) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
/* set a camera id */
|
||||
mDescription.v1.cameraId = id;
|
||||
|
||||
/* set camera metadata */
|
||||
mDescription.metadata.setToExternal((uint8_t *)camInfo->characteristics,
|
||||
mDescription.metadata.setToExternal((uint8_t*)camInfo->characteristics,
|
||||
get_camera_metadata_size(camInfo->characteristics));
|
||||
}
|
||||
|
||||
|
||||
EvsCamera::~EvsCamera() {
|
||||
ALOGD("EvsCamera being destroyed");
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
forceShutdown();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// This gets called if another caller "steals" ownership of the camera
|
||||
//
|
||||
void EvsCamera::forceShutdown()
|
||||
{
|
||||
ALOGD("EvsCamera forceShutdown");
|
||||
void EvsCamera::forceShutdown() {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
// Make sure our output stream is cleaned up
|
||||
// (It really should be already)
|
||||
stopVideoStream();
|
||||
|
||||
// Claim the lock while we work on internal state
|
||||
std::lock_guard <std::mutex> lock(mAccessLock);
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
|
||||
// Drop all the graphics buffers we've been using
|
||||
if (mBuffers.size() > 0) {
|
||||
@@ -96,19 +96,18 @@ void EvsCamera::forceShutdown()
|
||||
mStreamState = DEAD;
|
||||
}
|
||||
|
||||
|
||||
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsCamera follow.
|
||||
Return<void> EvsCamera::getCameraInfo(getCameraInfo_cb _hidl_cb) {
|
||||
ALOGD("getCameraInfo");
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
// Send back our self description
|
||||
_hidl_cb(mDescription.v1);
|
||||
return Void();
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
Return<EvsResult> EvsCamera::setMaxFramesInFlight(uint32_t bufferCount) {
|
||||
ALOGD("setMaxFramesInFlight");
|
||||
ALOGD("%s, bufferCount = %u", __FUNCTION__, bufferCount);
|
||||
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
|
||||
// If we've been displaced by another owner of the camera, then we can't do anything else
|
||||
@@ -131,9 +130,9 @@ Return<EvsResult> EvsCamera::setMaxFramesInFlight(uint32_t bufferCount) {
|
||||
}
|
||||
}
|
||||
|
||||
Return<EvsResult> EvsCamera::startVideoStream(const ::android::sp<V1_0::IEvsCameraStream>& stream) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
Return<EvsResult> EvsCamera::startVideoStream(const ::android::sp<IEvsCameraStream_1_0>& stream) {
|
||||
ALOGD("startVideoStream");
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
|
||||
// If we've been displaced by another owner of the camera, then we can't do anything else
|
||||
@@ -141,82 +140,86 @@ Return<EvsResult> EvsCamera::startVideoStream(const ::android::sp<IEvsCameraStre
|
||||
ALOGE("ignoring startVideoStream call when camera has been lost.");
|
||||
return EvsResult::OWNERSHIP_LOST;
|
||||
}
|
||||
|
||||
if (mStreamState != STOPPED) {
|
||||
ALOGE("ignoring startVideoStream call when a stream is already running.");
|
||||
return EvsResult::STREAM_ALREADY_RUNNING;
|
||||
}
|
||||
|
||||
// If the client never indicated otherwise, configure ourselves for a single streaming buffer
|
||||
if (mFramesAllowed < 1) {
|
||||
if (!setAvailableFrames_Locked(1)) {
|
||||
if (mFramesAllowed < kMinimumBuffersInFlight) {
|
||||
if (!setAvailableFrames_Locked(kMinimumBuffersInFlight)) {
|
||||
ALOGE("Failed to start stream because we couldn't get a graphics buffer");
|
||||
return EvsResult::BUFFER_NOT_AVAILABLE;
|
||||
}
|
||||
}
|
||||
|
||||
// Record the user's callback for use when we have a frame ready
|
||||
mStream = IEvsCameraStream_1_1::castFrom(stream).withDefault(nullptr);
|
||||
if (mStream == nullptr) {
|
||||
mStream = IEvsCameraStream::castFrom(stream).withDefault(nullptr);
|
||||
if (!mStream) {
|
||||
ALOGE("Default implementation does not support v1.0 IEvsCameraStream");
|
||||
return EvsResult::INVALID_ARG;
|
||||
}
|
||||
|
||||
// Start the frame generation thread
|
||||
mStreamState = RUNNING;
|
||||
mCaptureThread = std::thread([this](){ generateFrames(); });
|
||||
mCaptureThread = std::thread([this]() { generateFrames(); });
|
||||
|
||||
return EvsResult::OK;
|
||||
}
|
||||
|
||||
|
||||
Return<void> EvsCamera::doneWithFrame(const BufferDesc_1_0& buffer) {
|
||||
std::lock_guard <std::mutex> lock(mAccessLock);
|
||||
returnBuffer(buffer.bufferId, buffer.memHandle);
|
||||
|
||||
return Void();
|
||||
}
|
||||
|
||||
|
||||
Return<void> EvsCamera::stopVideoStream() {
|
||||
ALOGD("stopVideoStream");
|
||||
std::unique_lock <std::mutex> lock(mAccessLock);
|
||||
|
||||
if (mStreamState == RUNNING) {
|
||||
// Tell the GenerateFrames loop we want it to stop
|
||||
mStreamState = STOPPING;
|
||||
|
||||
// Block outside the mutex until the "stop" flag has been acknowledged
|
||||
// We won't send any more frames, but the client might still get some already in flight
|
||||
ALOGD("Waiting for stream thread to end...");
|
||||
lock.unlock();
|
||||
mCaptureThread.join();
|
||||
lock.lock();
|
||||
|
||||
mStreamState = STOPPED;
|
||||
mStream = nullptr;
|
||||
ALOGD("Stream marked STOPPED.");
|
||||
}
|
||||
|
||||
return Void();
|
||||
}
|
||||
|
||||
|
||||
Return<int32_t> EvsCamera::getExtendedInfo(uint32_t opaqueIdentifier) {
|
||||
ALOGD("getExtendedInfo");
|
||||
Return<void> EvsCamera::doneWithFrame(const V1_0::BufferDesc& buffer) {
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
returnBufferLocked(buffer.bufferId, buffer.memHandle);
|
||||
|
||||
// For any single digit value, return the index itself as a test value
|
||||
if (opaqueIdentifier <= 9) {
|
||||
return opaqueIdentifier;
|
||||
}
|
||||
|
||||
// Return zero by default as required by the spec
|
||||
return 0;
|
||||
return {};
|
||||
}
|
||||
|
||||
Return<void> EvsCamera::stopVideoStream() {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
std::unique_lock<std::mutex> lock(mAccessLock);
|
||||
|
||||
if (mStreamState != RUNNING) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Tell the GenerateFrames loop we want it to stop
|
||||
mStreamState = STOPPING;
|
||||
|
||||
// Block outside the mutex until the "stop" flag has been acknowledged
|
||||
// We won't send any more frames, but the client might still get some already in flight
|
||||
ALOGD("Waiting for stream thread to end...");
|
||||
lock.unlock();
|
||||
if (mCaptureThread.joinable()) {
|
||||
mCaptureThread.join();
|
||||
}
|
||||
lock.lock();
|
||||
|
||||
mStreamState = STOPPED;
|
||||
mStream = nullptr;
|
||||
ALOGD("Stream marked STOPPED.");
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Return<int32_t> EvsCamera::getExtendedInfo(uint32_t opaqueIdentifier) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
const auto it = mExtInfo.find(opaqueIdentifier);
|
||||
if (it == mExtInfo.end()) {
|
||||
// Return zero by default as required by the spec
|
||||
return 0;
|
||||
} else {
|
||||
return it->second[0];
|
||||
}
|
||||
}
|
||||
|
||||
Return<EvsResult> EvsCamera::setExtendedInfo([[maybe_unused]] uint32_t opaqueIdentifier,
|
||||
[[maybe_unused]] int32_t opaqueValue) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
Return<EvsResult> EvsCamera::setExtendedInfo(uint32_t /*opaqueIdentifier*/, int32_t /*opaqueValue*/) {
|
||||
ALOGD("setExtendedInfo");
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
|
||||
// If we've been displaced by another owner of the camera, then we can't do anything else
|
||||
@@ -225,76 +228,73 @@ Return<EvsResult> EvsCamera::setExtendedInfo(uint32_t /*opaqueIdentifier*/, int3
|
||||
return EvsResult::OWNERSHIP_LOST;
|
||||
}
|
||||
|
||||
// We don't store any device specific information in this implementation
|
||||
return EvsResult::INVALID_ARG;
|
||||
mExtInfo.insert_or_assign(opaqueIdentifier, opaqueValue);
|
||||
return EvsResult::OK;
|
||||
}
|
||||
|
||||
|
||||
// Methods from ::android::hardware::automotive::evs::V1_1::IEvsCamera follow.
|
||||
Return<void> EvsCamera::getCameraInfo_1_1(getCameraInfo_1_1_cb _hidl_cb) {
|
||||
ALOGD("getCameraInfo_1_1");
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
// Send back our self description
|
||||
_hidl_cb(mDescription);
|
||||
return Void();
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
Return<void> EvsCamera::getPhysicalCameraInfo(const hidl_string& id,
|
||||
Return<void> EvsCamera::getPhysicalCameraInfo([[maybe_unused]] const hidl_string& id,
|
||||
getCameraInfo_1_1_cb _hidl_cb) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
// This works exactly same as getCameraInfo_1_1() in default implementation.
|
||||
(void)id;
|
||||
_hidl_cb(mDescription);
|
||||
return Void();
|
||||
return {};
|
||||
}
|
||||
|
||||
Return<EvsResult> EvsCamera::doneWithFrame_1_1(const hidl_vec<BufferDesc>& buffers) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
Return<EvsResult> EvsCamera::doneWithFrame_1_1(const hidl_vec<BufferDesc_1_1>& buffers) {
|
||||
std::lock_guard <std::mutex> lock(mAccessLock);
|
||||
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
for (auto&& buffer : buffers) {
|
||||
returnBuffer(buffer.bufferId, buffer.buffer.nativeHandle);
|
||||
returnBufferLocked(buffer.bufferId, buffer.buffer.nativeHandle);
|
||||
}
|
||||
|
||||
return EvsResult::OK;
|
||||
}
|
||||
|
||||
|
||||
Return<EvsResult> EvsCamera::pauseVideoStream() {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
// Default implementation does not support this.
|
||||
return EvsResult::UNDERLYING_SERVICE_ERROR;
|
||||
}
|
||||
|
||||
|
||||
Return<EvsResult> EvsCamera::resumeVideoStream() {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
// Default implementation does not support this.
|
||||
return EvsResult::UNDERLYING_SERVICE_ERROR;
|
||||
}
|
||||
|
||||
|
||||
Return<EvsResult> EvsCamera::setMaster() {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
// Default implementation does not expect multiple subscribers and therefore
|
||||
// return a success code always.
|
||||
return EvsResult::OK;
|
||||
}
|
||||
|
||||
Return<EvsResult> EvsCamera::forceMaster(const sp<IEvsDisplay_1_0>& ) {
|
||||
Return<EvsResult> EvsCamera::forceMaster(const sp<V1_0::IEvsDisplay>&) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
// Default implementation does not expect multiple subscribers and therefore
|
||||
// return a success code always.
|
||||
return EvsResult::OK;
|
||||
}
|
||||
|
||||
|
||||
Return<EvsResult> EvsCamera::unsetMaster() {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
// Default implementation does not expect multiple subscribers and therefore
|
||||
// return a success code always.
|
||||
return EvsResult::OK;
|
||||
}
|
||||
|
||||
|
||||
Return<void> EvsCamera::getParameterList(getParameterList_cb _hidl_cb) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
hidl_vec<CameraParam> hidlCtrls;
|
||||
hidlCtrls.resize(mCameraInfo->controls.size());
|
||||
unsigned idx = 0;
|
||||
@@ -303,72 +303,132 @@ Return<void> EvsCamera::getParameterList(getParameterList_cb _hidl_cb) {
|
||||
}
|
||||
|
||||
_hidl_cb(hidlCtrls);
|
||||
return Void();
|
||||
}
|
||||
|
||||
|
||||
Return<void> EvsCamera::getIntParameterRange(CameraParam id,
|
||||
getIntParameterRange_cb _hidl_cb) {
|
||||
auto range = mCameraInfo->controls[id];
|
||||
_hidl_cb(get<0>(range), get<1>(range), get<2>(range));
|
||||
return Void();
|
||||
}
|
||||
|
||||
|
||||
Return<void> EvsCamera::setIntParameter(CameraParam id, int32_t value,
|
||||
setIntParameter_cb _hidl_cb) {
|
||||
// Default implementation does not support this.
|
||||
(void)id;
|
||||
(void)value;
|
||||
_hidl_cb(EvsResult::INVALID_ARG, 0);
|
||||
return Void();
|
||||
}
|
||||
|
||||
|
||||
Return<void> EvsCamera::getIntParameter(CameraParam id,
|
||||
getIntParameter_cb _hidl_cb) {
|
||||
// Default implementation does not support this.
|
||||
(void)id;
|
||||
_hidl_cb(EvsResult::INVALID_ARG, 0);
|
||||
return Void();
|
||||
}
|
||||
|
||||
|
||||
Return<EvsResult> EvsCamera::setExtendedInfo_1_1(uint32_t opaqueIdentifier,
|
||||
const hidl_vec<uint8_t>& opaqueValue) {
|
||||
// Default implementation does not use an extended info.
|
||||
(void)opaqueIdentifier;
|
||||
(void)opaqueValue;
|
||||
return EvsResult::INVALID_ARG;
|
||||
}
|
||||
|
||||
|
||||
Return<void> EvsCamera::getExtendedInfo_1_1(uint32_t opaqueIdentifier,
|
||||
getExtendedInfo_1_1_cb _hidl_cb) {
|
||||
// Default implementation does not use an extended info.
|
||||
(void)opaqueIdentifier;
|
||||
|
||||
hidl_vec<uint8_t> value;
|
||||
_hidl_cb(EvsResult::INVALID_ARG, value);
|
||||
return Void();
|
||||
}
|
||||
|
||||
|
||||
Return<void>
|
||||
EvsCamera::importExternalBuffers(const hidl_vec<BufferDesc_1_1>& /* buffers */,
|
||||
importExternalBuffers_cb _hidl_cb) {
|
||||
ALOGW("%s is not implemented yet.", __FUNCTION__);
|
||||
_hidl_cb(EvsResult::UNDERLYING_SERVICE_ERROR, 0);
|
||||
return {};
|
||||
}
|
||||
|
||||
Return<void> EvsCamera::getIntParameterRange(CameraParam id, getIntParameterRange_cb _hidl_cb) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
auto it = mCameraInfo->controls.find(id);
|
||||
if (it == mCameraInfo->controls.end()) {
|
||||
_hidl_cb(0, 0, 0);
|
||||
} else {
|
||||
_hidl_cb(std::get<0>(it->second), std::get<1>(it->second), std::get<2>(it->second));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Return<void> EvsCamera::setIntParameter(CameraParam id, int32_t value,
|
||||
setIntParameter_cb _hidl_cb) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
mParams.insert_or_assign(id, value);
|
||||
_hidl_cb(EvsResult::OK, {value});
|
||||
return {};
|
||||
}
|
||||
|
||||
Return<void> EvsCamera::getIntParameter([[maybe_unused]] CameraParam id,
|
||||
getIntParameter_cb _hidl_cb) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
auto it = mParams.find(id);
|
||||
std::vector<int32_t> values;
|
||||
if (it == mParams.end()) {
|
||||
_hidl_cb(EvsResult::INVALID_ARG, values);
|
||||
} else {
|
||||
values.push_back(it->second);
|
||||
_hidl_cb(EvsResult::OK, values);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Return<EvsResult> EvsCamera::setExtendedInfo_1_1(uint32_t opaqueIdentifier,
|
||||
const hidl_vec<uint8_t>& opaqueValue) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
mExtInfo.insert_or_assign(opaqueIdentifier, opaqueValue);
|
||||
return EvsResult::OK;
|
||||
}
|
||||
|
||||
Return<void> EvsCamera::getExtendedInfo_1_1(uint32_t opaqueIdentifier,
|
||||
getExtendedInfo_1_1_cb _hidl_cb) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
auto status = EvsResult::OK;
|
||||
hidl_vec<uint8_t> value;
|
||||
const auto it = mExtInfo.find(opaqueIdentifier);
|
||||
if (it == mExtInfo.end()) {
|
||||
status = EvsResult::INVALID_ARG;
|
||||
} else {
|
||||
value = it->second;
|
||||
}
|
||||
_hidl_cb(status, value);
|
||||
return {};
|
||||
}
|
||||
|
||||
Return<void> EvsCamera::importExternalBuffers([[maybe_unused]] const hidl_vec<BufferDesc>& buffers,
|
||||
importExternalBuffers_cb _hidl_cb) {
|
||||
auto numBuffersToAdd = buffers.size();
|
||||
if (numBuffersToAdd < 1) {
|
||||
ALOGD("No buffers to add");
|
||||
_hidl_cb(EvsResult::OK, mFramesAllowed);
|
||||
return {};
|
||||
}
|
||||
|
||||
{
|
||||
std::scoped_lock<std::mutex> lock(mAccessLock);
|
||||
|
||||
if (numBuffersToAdd > (kMaxBuffersInFlight - mFramesAllowed)) {
|
||||
numBuffersToAdd -= (kMaxBuffersInFlight - mFramesAllowed);
|
||||
ALOGW("Exceed the limit on number of buffers. %" PRIu64 " buffers will be added only.",
|
||||
static_cast<uint64_t>(numBuffersToAdd));
|
||||
}
|
||||
|
||||
GraphicBufferMapper& mapper = GraphicBufferMapper::get();
|
||||
const auto before = mFramesAllowed;
|
||||
for (auto i = 0; i < numBuffersToAdd; ++i) {
|
||||
// TODO: reject if external buffer is configured differently.
|
||||
auto& b = buffers[i];
|
||||
const AHardwareBuffer_Desc* pDesc =
|
||||
reinterpret_cast<const AHardwareBuffer_Desc*>(&b.buffer.description);
|
||||
|
||||
// Import a buffer to add
|
||||
buffer_handle_t memHandle = nullptr;
|
||||
status_t result =
|
||||
mapper.importBuffer(b.buffer.nativeHandle, pDesc->width, pDesc->height, 1,
|
||||
pDesc->format, pDesc->usage, pDesc->stride, &memHandle);
|
||||
if (result != android::NO_ERROR || !memHandle) {
|
||||
ALOGW("Failed to import a buffer %d", b.bufferId);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto stored = false;
|
||||
for (auto&& rec : mBuffers) {
|
||||
if (rec.handle == nullptr) {
|
||||
// Use this existing entry
|
||||
rec.handle = memHandle;
|
||||
rec.inUse = false;
|
||||
|
||||
stored = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!stored) {
|
||||
// Add a BufferRecord wrapping this handle to our set of available buffers
|
||||
mBuffers.emplace_back(memHandle);
|
||||
}
|
||||
|
||||
++mFramesAllowed;
|
||||
}
|
||||
|
||||
_hidl_cb(EvsResult::OK, mFramesAllowed - before);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
bool EvsCamera::setAvailableFrames_Locked(unsigned bufferCount) {
|
||||
if (bufferCount < 1) {
|
||||
ALOGE("Ignoring request to set buffer count to zero");
|
||||
if (bufferCount < kMinimumBuffersInFlight) {
|
||||
ALOGE("Ignoring request to set buffer count below the minimum number of buffers to run a "
|
||||
"video stream");
|
||||
return false;
|
||||
}
|
||||
if (bufferCount > MAX_BUFFERS_IN_FLIGHT) {
|
||||
if (bufferCount > kMaxBuffersInFlight) {
|
||||
ALOGE("Rejecting buffer request in excess of internal limit");
|
||||
return false;
|
||||
}
|
||||
@@ -403,17 +463,16 @@ bool EvsCamera::setAvailableFrames_Locked(unsigned bufferCount) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
unsigned EvsCamera::increaseAvailableFrames_Locked(unsigned numToAdd) {
|
||||
// Acquire the graphics buffer allocator
|
||||
GraphicBufferAllocator &alloc(GraphicBufferAllocator::get());
|
||||
GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
|
||||
|
||||
unsigned added = 0;
|
||||
|
||||
while (added < numToAdd) {
|
||||
buffer_handle_t memHandle = nullptr;
|
||||
status_t result = alloc.allocate(mWidth, mHeight, mFormat, 1, mUsage,
|
||||
&memHandle, &mStride, 0, "EvsCamera");
|
||||
status_t result = alloc.allocate(mWidth, mHeight, mFormat, 1, mUsage, &memHandle, &mStride,
|
||||
0, "EvsCamera");
|
||||
if (result != NO_ERROR) {
|
||||
ALOGE("Error %d allocating %d x %d graphics buffer", result, mWidth, mHeight);
|
||||
break;
|
||||
@@ -436,7 +495,7 @@ unsigned EvsCamera::increaseAvailableFrames_Locked(unsigned numToAdd) {
|
||||
}
|
||||
if (!stored) {
|
||||
// Add a BufferRecord wrapping this handle to our set of available buffers
|
||||
mBuffers.emplace_back(memHandle);
|
||||
mBuffers.push_back(std::move(BufferRecord(memHandle)));
|
||||
}
|
||||
|
||||
mFramesAllowed++;
|
||||
@@ -446,10 +505,9 @@ unsigned EvsCamera::increaseAvailableFrames_Locked(unsigned numToAdd) {
|
||||
return added;
|
||||
}
|
||||
|
||||
|
||||
unsigned EvsCamera::decreaseAvailableFrames_Locked(unsigned numToRemove) {
|
||||
// Acquire the graphics buffer allocator
|
||||
GraphicBufferAllocator &alloc(GraphicBufferAllocator::get());
|
||||
GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
|
||||
|
||||
unsigned removed = 0;
|
||||
|
||||
@@ -472,14 +530,12 @@ unsigned EvsCamera::decreaseAvailableFrames_Locked(unsigned numToRemove) {
|
||||
return removed;
|
||||
}
|
||||
|
||||
|
||||
// This is the asynchronous frame generation thread that runs in parallel with the
|
||||
// main serving thread. There is one for each active camera instance.
|
||||
void EvsCamera::generateFrames() {
|
||||
ALOGD("Frame generation loop started");
|
||||
|
||||
unsigned idx;
|
||||
|
||||
while (true) {
|
||||
bool timeForFrame = false;
|
||||
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
@@ -521,9 +577,9 @@ void EvsCamera::generateFrames() {
|
||||
|
||||
if (timeForFrame) {
|
||||
// Assemble the buffer description we'll transmit below
|
||||
BufferDesc_1_1 newBuffer = {};
|
||||
BufferDesc newBuffer = {};
|
||||
AHardwareBuffer_Desc* pDesc =
|
||||
reinterpret_cast<AHardwareBuffer_Desc *>(&newBuffer.buffer.description);
|
||||
reinterpret_cast<AHardwareBuffer_Desc*>(&newBuffer.buffer.description);
|
||||
pDesc->width = mWidth;
|
||||
pDesc->height = mHeight;
|
||||
pDesc->layers = 1;
|
||||
@@ -534,19 +590,16 @@ void EvsCamera::generateFrames() {
|
||||
newBuffer.pixelSize = sizeof(uint32_t);
|
||||
newBuffer.bufferId = idx;
|
||||
newBuffer.deviceId = mDescription.v1.cameraId;
|
||||
newBuffer.timestamp = elapsedRealtimeNano();
|
||||
newBuffer.timestamp = elapsedRealtimeNano() * 1e+3; // timestamps is in microseconds
|
||||
|
||||
// Write test data into the image buffer
|
||||
fillTestFrame(newBuffer);
|
||||
|
||||
// Issue the (asynchronous) callback to the client -- can't be holding the lock
|
||||
hidl_vec<BufferDesc_1_1> frames;
|
||||
frames.resize(1);
|
||||
frames[0] = newBuffer;
|
||||
auto result = mStream->deliverFrame_1_1(frames);
|
||||
auto result = mStream->deliverFrame_1_1({newBuffer});
|
||||
if (result.isOk()) {
|
||||
ALOGD("Delivered %p as id %d",
|
||||
newBuffer.buffer.nativeHandle.getNativeHandle(), newBuffer.bufferId);
|
||||
ALOGD("Delivered %p as id %d", newBuffer.buffer.nativeHandle.getNativeHandle(),
|
||||
newBuffer.bufferId);
|
||||
} else {
|
||||
// This can happen if the client dies and is likely unrecoverable.
|
||||
// To avoid consuming resources generating failing calls, we stop sending
|
||||
@@ -563,63 +616,50 @@ void EvsCamera::generateFrames() {
|
||||
}
|
||||
}
|
||||
|
||||
// We arbitrarily choose to generate frames at 12 fps to ensure we pass the 10fps test requirement
|
||||
static const int kTargetFrameRate = 12;
|
||||
static const nsecs_t kTargetFrameTimeUs = 1000*1000 / kTargetFrameRate;
|
||||
// We arbitrarily choose to generate frames at 15 fps to ensure we pass the 10fps test
|
||||
// requirement
|
||||
static const int kTargetFrameRate = 15;
|
||||
static const nsecs_t kTargetFrameIntervalUs = 1000 * 1000 / kTargetFrameRate;
|
||||
const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
const nsecs_t workTimeUs = (now - startTime) / 1000;
|
||||
const nsecs_t sleepDurationUs = kTargetFrameTimeUs - workTimeUs;
|
||||
const nsecs_t elapsedTimeUs = (now - startTime) / 1000;
|
||||
const nsecs_t sleepDurationUs = kTargetFrameIntervalUs - elapsedTimeUs;
|
||||
if (sleepDurationUs > 0) {
|
||||
usleep(sleepDurationUs);
|
||||
}
|
||||
}
|
||||
|
||||
// If we've been asked to stop, send an event to signal the actual end of stream
|
||||
EvsEventDesc event;
|
||||
event.aType = EvsEventType::STREAM_STOPPED;
|
||||
auto result = mStream->notify(event);
|
||||
if (!result.isOk()) {
|
||||
EvsEventDesc event = {
|
||||
.aType = EvsEventType::STREAM_STOPPED,
|
||||
};
|
||||
if (!mStream->notify(event).isOk()) {
|
||||
ALOGE("Error delivering end of stream marker");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void EvsCamera::fillTestFrame(const BufferDesc_1_1& buff) {
|
||||
void EvsCamera::fillTestFrame(const BufferDesc& buff) {
|
||||
// Lock our output buffer for writing
|
||||
uint32_t *pixels = nullptr;
|
||||
uint32_t* pixels = nullptr;
|
||||
const AHardwareBuffer_Desc* pDesc =
|
||||
reinterpret_cast<const AHardwareBuffer_Desc *>(&buff.buffer.description);
|
||||
GraphicBufferMapper &mapper = GraphicBufferMapper::get();
|
||||
reinterpret_cast<const AHardwareBuffer_Desc*>(&buff.buffer.description);
|
||||
GraphicBufferMapper& mapper = GraphicBufferMapper::get();
|
||||
mapper.lock(buff.buffer.nativeHandle,
|
||||
GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_NEVER,
|
||||
android::Rect(pDesc->width, pDesc->height),
|
||||
(void **) &pixels);
|
||||
android::Rect(pDesc->width, pDesc->height), (void**)&pixels);
|
||||
|
||||
// If we failed to lock the pixel buffer, we're about to crash, but log it first
|
||||
if (!pixels) {
|
||||
ALOGE("Camera failed to gain access to image buffer for writing");
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill in the test pixels
|
||||
// Fill in the test pixels; the colorbar in ABGR format
|
||||
for (unsigned row = 0; row < pDesc->height; row++) {
|
||||
for (unsigned col = 0; col < pDesc->width; col++) {
|
||||
// Index into the row to check the pixel at this column.
|
||||
// We expect 0xFF in the LSB channel, a vertical gradient in the
|
||||
// second channel, a horitzontal gradient in the third channel, and
|
||||
// 0xFF in the MSB.
|
||||
// The exception is the very first 32 bits which is used for the
|
||||
// time varying frame signature to avoid getting fooled by a static image.
|
||||
uint32_t expectedPixel = 0xFF0000FF | // MSB and LSB
|
||||
((row & 0xFF) << 8) | // vertical gradient
|
||||
((col & 0xFF) << 16); // horizontal gradient
|
||||
if ((row | col) == 0) {
|
||||
static uint32_t sFrameTicker = 0;
|
||||
expectedPixel = (sFrameTicker) & 0xFF;
|
||||
sFrameTicker++;
|
||||
}
|
||||
pixels[col] = expectedPixel;
|
||||
const uint32_t index = col * kNumColors / pDesc->width;
|
||||
pixels[col] = kColors[index];
|
||||
}
|
||||
// Point to the next row
|
||||
// NOTE: stride retrieved from gralloc is in units of pixels
|
||||
@@ -630,39 +670,35 @@ void EvsCamera::fillTestFrame(const BufferDesc_1_1& buff) {
|
||||
mapper.unlock(buff.buffer.nativeHandle);
|
||||
}
|
||||
|
||||
|
||||
void EvsCamera::fillTestFrame(const BufferDesc_1_0& buff) {
|
||||
BufferDesc_1_1 newBufDesc = {};
|
||||
AHardwareBuffer_Desc desc = {
|
||||
buff.width, // width
|
||||
buff.height, // height
|
||||
1, // layers, always 1 for EVS
|
||||
buff.format, // One of AHardwareBuffer_Format
|
||||
buff.usage, // Combination of AHardwareBuffer_UsageFlags
|
||||
buff.stride, // Row stride in pixels
|
||||
0, // Reserved
|
||||
0 // Reserved
|
||||
void EvsCamera::fillTestFrame(const V1_0::BufferDesc& buff) {
|
||||
BufferDesc newBuffer = {
|
||||
.buffer.nativeHandle = buff.memHandle,
|
||||
.pixelSize = buff.pixelSize,
|
||||
.bufferId = buff.bufferId,
|
||||
};
|
||||
memcpy(&desc, &newBufDesc.buffer.description, sizeof(desc));
|
||||
newBufDesc.buffer.nativeHandle = buff.memHandle;
|
||||
newBufDesc.pixelSize = buff.pixelSize;
|
||||
newBufDesc.bufferId = buff.bufferId;
|
||||
|
||||
return fillTestFrame(newBufDesc);
|
||||
AHardwareBuffer_Desc* pDesc =
|
||||
reinterpret_cast<AHardwareBuffer_Desc*>(&newBuffer.buffer.description);
|
||||
*pDesc = {
|
||||
buff.width, // width
|
||||
buff.height, // height
|
||||
1, // layers, always 1 for EVS
|
||||
buff.format, // One of AHardwareBuffer_Format
|
||||
buff.usage, // Combination of AHardwareBuffer_UsageFlags
|
||||
buff.stride, // Row stride in pixels
|
||||
0, // Reserved
|
||||
0 // Reserved
|
||||
};
|
||||
return fillTestFrame(newBuffer);
|
||||
}
|
||||
|
||||
|
||||
void EvsCamera::returnBuffer(const uint32_t bufferId, const buffer_handle_t memHandle) {
|
||||
std::lock_guard <std::mutex> lock(mAccessLock);
|
||||
|
||||
void EvsCamera::returnBufferLocked(const uint32_t bufferId, const buffer_handle_t memHandle) {
|
||||
if (memHandle == nullptr) {
|
||||
ALOGE("ignoring doneWithFrame called with null handle");
|
||||
} else if (bufferId >= mBuffers.size()) {
|
||||
ALOGE("ignoring doneWithFrame called with invalid bufferId %d (max is %zu)",
|
||||
bufferId, mBuffers.size()-1);
|
||||
ALOGE("ignoring doneWithFrame called with invalid bufferId %d (max is %zu)", bufferId,
|
||||
mBuffers.size() - 1);
|
||||
} else if (!mBuffers[bufferId].inUse) {
|
||||
ALOGE("ignoring doneWithFrame called on frame %d which is already free",
|
||||
bufferId);
|
||||
ALOGE("ignoring doneWithFrame called on frame %d which is already free", bufferId);
|
||||
} else {
|
||||
// Mark the frame as available
|
||||
mBuffers[bufferId].inUse = false;
|
||||
@@ -683,42 +719,33 @@ void EvsCamera::returnBuffer(const uint32_t bufferId, const buffer_handle_t memH
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sp<EvsCamera> EvsCamera::Create(const char *deviceName) {
|
||||
unique_ptr<ConfigManager::CameraInfo> nullCamInfo = nullptr;
|
||||
sp<EvsCamera> EvsCamera::Create(const char* deviceName) {
|
||||
std::unique_ptr<ConfigManager::CameraInfo> nullCamInfo = nullptr;
|
||||
|
||||
return Create(deviceName, nullCamInfo);
|
||||
}
|
||||
|
||||
|
||||
sp<EvsCamera> EvsCamera::Create(const char *deviceName,
|
||||
unique_ptr<ConfigManager::CameraInfo> &camInfo,
|
||||
const Stream *streamCfg) {
|
||||
sp<EvsCamera> EvsCamera::Create(const char* deviceName,
|
||||
std::unique_ptr<ConfigManager::CameraInfo>& camInfo,
|
||||
[[maybe_unused]] const Stream* streamCfg) {
|
||||
sp<EvsCamera> evsCamera = new EvsCamera(deviceName, camInfo);
|
||||
if (evsCamera == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* default implementation does not use a given configuration */
|
||||
(void)streamCfg;
|
||||
|
||||
/* Use the first resolution from the list for the testing */
|
||||
// Use the first resolution from the list for the testing
|
||||
// TODO(b/214835237): Uses a given Stream configuration to choose the best
|
||||
// stream configuration.
|
||||
auto it = camInfo->streamConfigurations.begin();
|
||||
evsCamera->mWidth = it->second[1];
|
||||
evsCamera->mHeight = it->second[2];
|
||||
evsCamera->mDescription.v1.vendorFlags = 0xFFFFFFFF; // Arbitrary test value
|
||||
evsCamera->mDescription.v1.vendorFlags = 0xFFFFFFFF; // Arbitrary test value
|
||||
|
||||
evsCamera->mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
|
||||
evsCamera->mUsage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_CAMERA_WRITE |
|
||||
GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_RARELY;
|
||||
evsCamera->mUsage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_CAMERA_WRITE |
|
||||
GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_RARELY;
|
||||
|
||||
return evsCamera;
|
||||
}
|
||||
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace evs
|
||||
} // namespace automotive
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace android::hardware::automotive::evs::V1_1::implementation
|
||||
|
||||
@@ -17,91 +17,70 @@
|
||||
#ifndef ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSCAMERA_H
|
||||
#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSCAMERA_H
|
||||
|
||||
#include <android/hardware/automotive/evs/1.1/types.h>
|
||||
#include "ConfigManager.h"
|
||||
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsCamera.h>
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsCameraStream.h>
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsDisplay.h>
|
||||
#include <android/hardware/automotive/evs/1.1/types.h>
|
||||
#include <ui/GraphicBuffer.h>
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "ConfigManager.h"
|
||||
|
||||
using BufferDesc_1_0 = ::android::hardware::automotive::evs::V1_0::BufferDesc;
|
||||
using BufferDesc_1_1 = ::android::hardware::automotive::evs::V1_1::BufferDesc;
|
||||
using IEvsCameraStream_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsCameraStream;
|
||||
using IEvsCameraStream_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsCameraStream;
|
||||
using ::android::hardware::automotive::evs::V1_0::EvsResult;
|
||||
using ::android::hardware::automotive::evs::V1_0::CameraDesc;
|
||||
using IEvsDisplay_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsDisplay;
|
||||
using IEvsDisplay_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsDisplay;
|
||||
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace automotive {
|
||||
namespace evs {
|
||||
namespace V1_1 {
|
||||
namespace implementation {
|
||||
|
||||
namespace android::hardware::automotive::evs::V1_1::implementation {
|
||||
|
||||
// From EvsEnumerator.h
|
||||
class EvsEnumerator;
|
||||
|
||||
|
||||
class EvsCamera : public IEvsCamera {
|
||||
public:
|
||||
public:
|
||||
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsCamera follow.
|
||||
Return<void> getCameraInfo(getCameraInfo_cb _hidl_cb) override;
|
||||
Return<EvsResult> setMaxFramesInFlight(uint32_t bufferCount) override;
|
||||
Return<EvsResult> startVideoStream(const ::android::sp<IEvsCameraStream_1_0>& stream) override;
|
||||
Return<void> stopVideoStream() override;
|
||||
Return<void> doneWithFrame(const BufferDesc_1_0& buffer) override;
|
||||
Return<void> getCameraInfo(getCameraInfo_cb _hidl_cb) override;
|
||||
Return<V1_0::EvsResult> setMaxFramesInFlight(uint32_t bufferCount) override;
|
||||
Return<V1_0::EvsResult> startVideoStream(const sp<V1_0::IEvsCameraStream>& stream) override;
|
||||
Return<void> stopVideoStream() override;
|
||||
Return<void> doneWithFrame(const V1_0::BufferDesc& buffer) override;
|
||||
|
||||
Return<int32_t> getExtendedInfo(uint32_t opaqueIdentifier) override;
|
||||
Return<EvsResult> setExtendedInfo(uint32_t opaqueIdentifier, int32_t opaqueValue) override;
|
||||
Return<int32_t> getExtendedInfo(uint32_t opaqueIdentifier) override;
|
||||
Return<V1_0::EvsResult> setExtendedInfo(uint32_t opaqueIdentifier,
|
||||
int32_t opaqueValue) override;
|
||||
|
||||
// Methods from ::android::hardware::automotive::evs::V1_1::IEvsCamera follow.
|
||||
Return<void> getCameraInfo_1_1(getCameraInfo_1_1_cb _hidl_cb) override;
|
||||
Return<void> getPhysicalCameraInfo(const hidl_string& id,
|
||||
getPhysicalCameraInfo_cb _hidl_cb) override;
|
||||
Return<EvsResult> pauseVideoStream() override;
|
||||
Return<EvsResult> resumeVideoStream() override;
|
||||
Return<EvsResult> doneWithFrame_1_1(const hidl_vec<BufferDesc_1_1>& buffer) override;
|
||||
Return<EvsResult> setMaster() override;
|
||||
Return<EvsResult> forceMaster(const sp<IEvsDisplay_1_0>& display) override;
|
||||
Return<EvsResult> unsetMaster() override;
|
||||
Return<void> getParameterList(getParameterList_cb _hidl_cb) override;
|
||||
Return<void> getIntParameterRange(CameraParam id,
|
||||
getIntParameterRange_cb _hidl_cb) override;
|
||||
Return<void> setIntParameter(CameraParam id, int32_t value,
|
||||
setIntParameter_cb _hidl_cb) override;
|
||||
Return<void> getIntParameter(CameraParam id,
|
||||
getIntParameter_cb _hidl_cb) override;
|
||||
Return<EvsResult> setExtendedInfo_1_1(uint32_t opaqueIdentifier,
|
||||
const hidl_vec<uint8_t>& opaqueValue) override;
|
||||
Return<void> getExtendedInfo_1_1(uint32_t opaqueIdentifier,
|
||||
getExtendedInfo_1_1_cb _hidl_cb) override;
|
||||
Return<void> importExternalBuffers(const hidl_vec<BufferDesc_1_1>& buffers,
|
||||
importExternalBuffers_cb _hidl_cb) override;
|
||||
Return<void> getCameraInfo_1_1(getCameraInfo_1_1_cb _hidl_cb) override;
|
||||
Return<void> getPhysicalCameraInfo(const hidl_string& id,
|
||||
getPhysicalCameraInfo_cb _hidl_cb) override;
|
||||
Return<V1_0::EvsResult> pauseVideoStream() override;
|
||||
Return<V1_0::EvsResult> resumeVideoStream() override;
|
||||
Return<V1_0::EvsResult> doneWithFrame_1_1(const hidl_vec<BufferDesc>& buffer) override;
|
||||
Return<V1_0::EvsResult> setMaster() override;
|
||||
Return<V1_0::EvsResult> forceMaster(const sp<V1_0::IEvsDisplay>& display) override;
|
||||
Return<V1_0::EvsResult> unsetMaster() override;
|
||||
Return<void> getParameterList(getParameterList_cb _hidl_cb) override;
|
||||
Return<void> getIntParameterRange(CameraParam id, getIntParameterRange_cb _hidl_cb) override;
|
||||
Return<void> setIntParameter(CameraParam id, int32_t value,
|
||||
setIntParameter_cb _hidl_cb) override;
|
||||
Return<void> getIntParameter(CameraParam id, getIntParameter_cb _hidl_cb) override;
|
||||
Return<V1_0::EvsResult> setExtendedInfo_1_1(uint32_t opaqueIdentifier,
|
||||
const hidl_vec<uint8_t>& opaqueValue) override;
|
||||
Return<void> getExtendedInfo_1_1(uint32_t opaqueIdentifier,
|
||||
getExtendedInfo_1_1_cb _hidl_cb) override;
|
||||
Return<void> importExternalBuffers(const hidl_vec<BufferDesc>& buffers,
|
||||
importExternalBuffers_cb _hidl_cb) override;
|
||||
|
||||
static sp<EvsCamera> Create(const char *deviceName);
|
||||
static sp<EvsCamera> Create(const char *deviceName,
|
||||
unique_ptr<ConfigManager::CameraInfo> &camInfo,
|
||||
const Stream *streamCfg = nullptr);
|
||||
static sp<EvsCamera> Create(const char* deviceName);
|
||||
static sp<EvsCamera> Create(const char* deviceName,
|
||||
std::unique_ptr<ConfigManager::CameraInfo>& camInfo,
|
||||
const Stream* streamCfg = nullptr);
|
||||
EvsCamera(const EvsCamera&) = delete;
|
||||
EvsCamera& operator=(const EvsCamera&) = delete;
|
||||
|
||||
virtual ~EvsCamera() override;
|
||||
void forceShutdown(); // This gets called if another caller "steals" ownership of the camera
|
||||
void forceShutdown(); // This gets called if another caller "steals" ownership of the camera
|
||||
|
||||
const CameraDesc& getDesc() { return mDescription; };
|
||||
|
||||
static const char kCameraName_Backup[];
|
||||
|
||||
private:
|
||||
EvsCamera(const char *id,
|
||||
unique_ptr<ConfigManager::CameraInfo> &camInfo);
|
||||
private:
|
||||
EvsCamera(const char* id, std::unique_ptr<ConfigManager::CameraInfo>& camInfo);
|
||||
// These three functions are expected to be called while mAccessLock is held
|
||||
//
|
||||
bool setAvailableFrames_Locked(unsigned bufferCount);
|
||||
@@ -109,34 +88,34 @@ private:
|
||||
unsigned decreaseAvailableFrames_Locked(unsigned numToRemove);
|
||||
|
||||
void generateFrames();
|
||||
void fillTestFrame(const BufferDesc_1_0& buff);
|
||||
void fillTestFrame(const BufferDesc_1_1& buff);
|
||||
void returnBuffer(const uint32_t bufferId, const buffer_handle_t memHandle);
|
||||
void fillTestFrame(const V1_0::BufferDesc& buff);
|
||||
void fillTestFrame(const BufferDesc& buff);
|
||||
void returnBufferLocked(const uint32_t bufferId, const buffer_handle_t memHandle);
|
||||
|
||||
sp<EvsEnumerator> mEnumerator; // The enumerator object that created this camera
|
||||
|
||||
CameraDesc mDescription = {}; // The properties of this camera
|
||||
CameraDesc mDescription = {}; // The properties of this camera
|
||||
|
||||
std::thread mCaptureThread; // The thread we'll use to synthesize frames
|
||||
std::thread mCaptureThread; // The thread we'll use to synthesize frames
|
||||
|
||||
uint32_t mWidth = 0; // Horizontal pixel count in the buffers
|
||||
uint32_t mHeight = 0; // Vertical pixel count in the buffers
|
||||
uint32_t mFormat = 0; // Values from android_pixel_format_t
|
||||
uint64_t mUsage = 0; // Values from from Gralloc.h
|
||||
uint32_t mStride = 0; // Bytes per line in the buffers
|
||||
uint32_t mWidth = 0; // Horizontal pixel count in the buffers
|
||||
uint32_t mHeight = 0; // Vertical pixel count in the buffers
|
||||
uint32_t mFormat = 0; // Values from android_pixel_format_t
|
||||
uint64_t mUsage = 0; // Values from from Gralloc.h
|
||||
uint32_t mStride = 0; // Bytes per line in the buffers
|
||||
|
||||
sp<IEvsCameraStream_1_1> mStream = nullptr; // The callback used to deliver each frame
|
||||
sp<IEvsCameraStream> mStream = nullptr; // The callback used to deliver each frame
|
||||
|
||||
struct BufferRecord {
|
||||
buffer_handle_t handle;
|
||||
bool inUse;
|
||||
|
||||
explicit BufferRecord(buffer_handle_t h) : handle(h), inUse(false) {};
|
||||
explicit BufferRecord(buffer_handle_t h) : handle(h), inUse(false){};
|
||||
};
|
||||
|
||||
std::vector <BufferRecord> mBuffers; // Graphics buffers to transfer images
|
||||
unsigned mFramesAllowed; // How many buffers are we currently using
|
||||
unsigned mFramesInUse; // How many buffers are currently outstanding
|
||||
std::vector<BufferRecord> mBuffers; // Graphics buffers to transfer images
|
||||
unsigned mFramesAllowed; // How many buffers are we currently using
|
||||
unsigned mFramesInUse; // How many buffers are currently outstanding
|
||||
|
||||
enum StreamStateValues {
|
||||
STOPPED,
|
||||
@@ -150,14 +129,13 @@ private:
|
||||
std::mutex mAccessLock;
|
||||
|
||||
// Static camera module information
|
||||
unique_ptr<ConfigManager::CameraInfo> &mCameraInfo;
|
||||
std::unique_ptr<ConfigManager::CameraInfo>& mCameraInfo;
|
||||
|
||||
// For the extended info
|
||||
std::unordered_map<uint32_t, hidl_vec<uint8_t>> mExtInfo;
|
||||
std::unordered_map<CameraParam, int32_t> mParams;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_1
|
||||
} // namespace evs
|
||||
} // namespace automotive
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace android::hardware::automotive::evs::V1_1::implementation
|
||||
|
||||
#endif // ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSCAMERA_H
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "android.hardware.automotive.evs@1.1-service"
|
||||
|
||||
#include "EvsDisplay.h"
|
||||
|
||||
#include <ui/GraphicBufferAllocator.h>
|
||||
@@ -23,51 +21,53 @@
|
||||
|
||||
using ::android::frameworks::automotive::display::V1_0::HwDisplayConfig;
|
||||
using ::android::frameworks::automotive::display::V1_0::HwDisplayState;
|
||||
using ::android::frameworks::automotive::display::V1_0::IAutomotiveDisplayProxyService;
|
||||
using ::android::hardware::automotive::evs::V1_0::DisplayDesc;
|
||||
using ::android::hardware::automotive::evs::V1_0::DisplayState;
|
||||
using ::android::hardware::automotive::evs::V1_0::EvsResult;
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace automotive {
|
||||
namespace evs {
|
||||
namespace V1_1 {
|
||||
namespace implementation {
|
||||
namespace {
|
||||
|
||||
// Arbitrary magic number for self-recognition
|
||||
constexpr uint32_t kDefaultDisplayBufferId = 0x3870;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace android::hardware::automotive::evs::V1_1::implementation {
|
||||
|
||||
EvsDisplay::EvsDisplay() {
|
||||
EvsDisplay(nullptr, 0);
|
||||
}
|
||||
|
||||
|
||||
EvsDisplay::EvsDisplay(sp<IAutomotiveDisplayProxyService> pDisplayProxy, uint64_t displayId)
|
||||
: mDisplayProxy(pDisplayProxy),
|
||||
mDisplayId(displayId) {
|
||||
mDisplayId(displayId),
|
||||
mGlWrapper(std::make_unique<GlWrapper>()) {
|
||||
ALOGD("EvsDisplay instantiated");
|
||||
|
||||
// Set up our self description
|
||||
// NOTE: These are arbitrary values chosen for testing
|
||||
mInfo.displayId = "Mock Display";
|
||||
mInfo.vendorFlags = 3870;
|
||||
mInfo.displayId = "Mock Display";
|
||||
mInfo.vendorFlags = 3870;
|
||||
|
||||
// Assemble the buffer description we'll use for our render target
|
||||
mBuffer.width = 320;
|
||||
mBuffer.height = 240;
|
||||
mBuffer.format = HAL_PIXEL_FORMAT_RGBA_8888;
|
||||
mBuffer.usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER;
|
||||
mBuffer.bufferId = 0x3870; // Arbitrary magic number for self recognition
|
||||
mBuffer.pixelSize = 4;
|
||||
mBuffer.width = 640;
|
||||
mBuffer.height = 360;
|
||||
mBuffer.format = HAL_PIXEL_FORMAT_RGBA_8888;
|
||||
mBuffer.usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER;
|
||||
mBuffer.bufferId = kDefaultDisplayBufferId;
|
||||
mBuffer.pixelSize = 4;
|
||||
}
|
||||
|
||||
|
||||
EvsDisplay::~EvsDisplay() {
|
||||
ALOGD("EvsDisplay being destroyed");
|
||||
forceShutdown();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This gets called if another caller "steals" ownership of the display
|
||||
*/
|
||||
void EvsDisplay::forceShutdown()
|
||||
{
|
||||
void EvsDisplay::forceShutdown() {
|
||||
ALOGD("EvsDisplay forceShutdown");
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
|
||||
@@ -84,6 +84,11 @@ void EvsDisplay::forceShutdown()
|
||||
GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
|
||||
alloc.free(mBuffer.memHandle);
|
||||
mBuffer.memHandle = nullptr;
|
||||
|
||||
if (mGlWrapper) {
|
||||
mGlWrapper->hideWindow(mDisplayProxy, mDisplayId);
|
||||
mGlWrapper->shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
// Put this object into an unrecoverable error state since somebody else
|
||||
@@ -91,20 +96,18 @@ void EvsDisplay::forceShutdown()
|
||||
mRequestedState = DisplayState::DEAD;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns basic information about the EVS display provided by the system.
|
||||
* See the description of the DisplayDesc structure for details.
|
||||
*/
|
||||
Return<void> EvsDisplay::getDisplayInfo(getDisplayInfo_cb _hidl_cb) {
|
||||
Return<void> EvsDisplay::getDisplayInfo(getDisplayInfo_cb _hidl_cb) {
|
||||
ALOGD("getDisplayInfo");
|
||||
|
||||
// Send back our self description
|
||||
_hidl_cb(mInfo);
|
||||
return Void();
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clients may set the display state to express their desired state.
|
||||
* The HAL implementation must gracefully accept a request for any state
|
||||
@@ -124,18 +127,28 @@ Return<EvsResult> EvsDisplay::setDisplayState(DisplayState state) {
|
||||
}
|
||||
|
||||
// Ensure we recognize the requested state so we don't go off the rails
|
||||
if (state < DisplayState::NUM_STATES) {
|
||||
// Record the requested state
|
||||
mRequestedState = state;
|
||||
return EvsResult::OK;
|
||||
}
|
||||
else {
|
||||
// Turn off the display if asked for an unrecognized state
|
||||
mRequestedState = DisplayState::NOT_VISIBLE;
|
||||
if (state >= DisplayState::NUM_STATES) {
|
||||
return EvsResult::INVALID_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mGlWrapper) {
|
||||
switch (state) {
|
||||
case DisplayState::NOT_VISIBLE:
|
||||
mGlWrapper->hideWindow(mDisplayProxy, mDisplayId);
|
||||
break;
|
||||
case DisplayState::VISIBLE:
|
||||
mGlWrapper->showWindow(mDisplayProxy, mDisplayId);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Record the requested state
|
||||
mRequestedState = state;
|
||||
|
||||
return EvsResult::OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* The HAL implementation should report the actual current state, which might
|
||||
@@ -144,14 +157,13 @@ Return<EvsResult> EvsDisplay::setDisplayState(DisplayState state) {
|
||||
* the device layer, making it undesirable for the HAL implementation to
|
||||
* spontaneously change display states.
|
||||
*/
|
||||
Return<DisplayState> EvsDisplay::getDisplayState() {
|
||||
Return<DisplayState> EvsDisplay::getDisplayState() {
|
||||
ALOGD("getDisplayState");
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
|
||||
return mRequestedState;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This call returns a handle to a frame buffer associated with the display.
|
||||
* This buffer may be locked and written to by software and/or GL. This buffer
|
||||
@@ -159,43 +171,55 @@ Return<DisplayState> EvsDisplay::getDisplayState() {
|
||||
* display is no longer visible.
|
||||
*/
|
||||
// TODO: We need to know if/when our client dies so we can get the buffer back! (blocked b/31632518)
|
||||
Return<void> EvsDisplay::getTargetBuffer(getTargetBuffer_cb _hidl_cb) {
|
||||
Return<void> EvsDisplay::getTargetBuffer(getTargetBuffer_cb _hidl_cb) {
|
||||
ALOGD("getTargetBuffer");
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
|
||||
if (mRequestedState == DisplayState::DEAD) {
|
||||
ALOGE("Rejecting buffer request from object that lost ownership of the display.");
|
||||
BufferDesc_1_0 nullBuff = {};
|
||||
_hidl_cb(nullBuff);
|
||||
return Void();
|
||||
_hidl_cb({});
|
||||
return {};
|
||||
}
|
||||
|
||||
// If we don't already have a buffer, allocate one now
|
||||
if (!mBuffer.memHandle) {
|
||||
// Initialize our display window
|
||||
// NOTE: This will cause the display to become "VISIBLE" before a frame is actually
|
||||
// returned, which is contrary to the spec and will likely result in a black frame being
|
||||
// (briefly) shown.
|
||||
if (mGlWrapper->initialize(mDisplayProxy, mDisplayId)) {
|
||||
// Assemble the buffer description we'll use for our render target
|
||||
mBuffer.width = mGlWrapper->getWidth();
|
||||
mBuffer.height = mGlWrapper->getHeight();
|
||||
mBuffer.format = HAL_PIXEL_FORMAT_RGBA_8888;
|
||||
mBuffer.usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER;
|
||||
mBuffer.bufferId = kDefaultDisplayBufferId;
|
||||
mBuffer.pixelSize = 4;
|
||||
} else {
|
||||
// If we failed to initialize a EGL, then we're not going to display
|
||||
// any.
|
||||
mGlWrapper = nullptr;
|
||||
}
|
||||
|
||||
// Allocate the buffer that will hold our displayable image
|
||||
buffer_handle_t handle = nullptr;
|
||||
GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
|
||||
status_t result = alloc.allocate(
|
||||
mBuffer.width, mBuffer.height, mBuffer.format, 1, mBuffer.usage,
|
||||
&handle, &mBuffer.stride, 0, "EvsDisplay");
|
||||
if (result != NO_ERROR) {
|
||||
ALOGE("Error %d allocating %d x %d graphics buffer",
|
||||
result, mBuffer.width, mBuffer.height);
|
||||
BufferDesc_1_0 nullBuff = {};
|
||||
_hidl_cb(nullBuff);
|
||||
return Void();
|
||||
}
|
||||
if (!handle) {
|
||||
ALOGE("We didn't get a buffer handle back from the allocator");
|
||||
BufferDesc_1_0 nullBuff = {};
|
||||
_hidl_cb(nullBuff);
|
||||
return Void();
|
||||
status_t result = alloc.allocate(mBuffer.width, mBuffer.height, mBuffer.format, 1,
|
||||
mBuffer.usage, &handle, &mBuffer.stride, 0, "EvsDisplay");
|
||||
if (result != NO_ERROR || !handle) {
|
||||
ALOGE("Error %d allocating %d x %d graphics buffer", result, mBuffer.width,
|
||||
mBuffer.height);
|
||||
if (mGlWrapper) {
|
||||
mGlWrapper->shutdown();
|
||||
}
|
||||
_hidl_cb({});
|
||||
return {};
|
||||
}
|
||||
|
||||
mBuffer.memHandle = handle;
|
||||
mFrameBusy = false;
|
||||
ALOGD("Allocated new buffer %p with stride %u",
|
||||
mBuffer.memHandle.getNativeHandle(), mBuffer.stride);
|
||||
ALOGD("Allocated new buffer %p with stride %u", mBuffer.memHandle.getNativeHandle(),
|
||||
mBuffer.stride);
|
||||
}
|
||||
|
||||
// Do we have a frame available?
|
||||
@@ -205,41 +229,40 @@ Return<void> EvsDisplay::getTargetBuffer(getTargetBuffer_cb _hidl_cb) {
|
||||
// a previously issued buffer yet (they're behaving badly).
|
||||
// NOTE: We have to make the callback even if we have nothing to provide
|
||||
ALOGE("getTargetBuffer called while no buffers available.");
|
||||
BufferDesc_1_0 nullBuff = {};
|
||||
_hidl_cb(nullBuff);
|
||||
return Void();
|
||||
_hidl_cb({});
|
||||
return {};
|
||||
} else {
|
||||
// Mark our buffer as busy
|
||||
mFrameBusy = true;
|
||||
|
||||
// Send the buffer to the client
|
||||
ALOGD("Providing display buffer handle %p as id %d",
|
||||
mBuffer.memHandle.getNativeHandle(), mBuffer.bufferId);
|
||||
ALOGD("Providing display buffer handle %p as id %d", mBuffer.memHandle.getNativeHandle(),
|
||||
mBuffer.bufferId);
|
||||
_hidl_cb(mBuffer);
|
||||
return Void();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This call tells the display that the buffer is ready for display.
|
||||
* The buffer is no longer valid for use by the client after this call.
|
||||
*/
|
||||
Return<EvsResult> EvsDisplay::returnTargetBufferForDisplayImpl(const uint32_t bufferId, const buffer_handle_t memHandle) {
|
||||
Return<EvsResult> EvsDisplay::returnTargetBufferForDisplayImpl(const uint32_t bufferId,
|
||||
const buffer_handle_t memHandle) {
|
||||
ALOGD("returnTargetBufferForDisplay %p", memHandle);
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
|
||||
// Nobody should call us with a null handle
|
||||
if (!memHandle) {
|
||||
ALOGE ("returnTargetBufferForDisplay called without a valid buffer handle.\n");
|
||||
ALOGE("returnTargetBufferForDisplay called without a valid buffer handle.\n");
|
||||
return EvsResult::INVALID_ARG;
|
||||
}
|
||||
if (bufferId != mBuffer.bufferId) {
|
||||
ALOGE ("Got an unrecognized frame returned.\n");
|
||||
ALOGE("Got an unrecognized frame returned.\n");
|
||||
return EvsResult::INVALID_ARG;
|
||||
}
|
||||
if (!mFrameBusy) {
|
||||
ALOGE ("A frame was returned with no outstanding frames.\n");
|
||||
ALOGE("A frame was returned with no outstanding frames.\n");
|
||||
return EvsResult::BUFFER_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
@@ -253,23 +276,32 @@ Return<EvsResult> EvsDisplay::returnTargetBufferForDisplayImpl(const uint32_t bu
|
||||
// If we were waiting for a new frame, this is it!
|
||||
if (mRequestedState == DisplayState::VISIBLE_ON_NEXT_FRAME) {
|
||||
mRequestedState = DisplayState::VISIBLE;
|
||||
if (mGlWrapper) {
|
||||
mGlWrapper->showWindow(mDisplayProxy, mDisplayId);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate we're in an expected state
|
||||
if (mRequestedState != DisplayState::VISIBLE) {
|
||||
// We shouldn't get frames back when we're not visible.
|
||||
ALOGE ("Got an unexpected frame returned while not visible - ignoring.\n");
|
||||
} else {
|
||||
ALOGE("Got an unexpected frame returned while not visible - ignoring.\n");
|
||||
} else if (mGlWrapper) {
|
||||
// This is where the buffer would be made visible.
|
||||
// For now we simply validate it has the data we expect in it by reading it back
|
||||
if (!mGlWrapper->updateImageTexture(mBuffer)) {
|
||||
return EvsResult::UNDERLYING_SERVICE_ERROR;
|
||||
}
|
||||
|
||||
// Put the image on the screen
|
||||
mGlWrapper->renderImageToScreen();
|
||||
} else {
|
||||
// TODO: Move below validation logic to somewhere else
|
||||
#if 0
|
||||
// For now we simply validate it has the data we expect in it by reading it back
|
||||
// Lock our display buffer for reading
|
||||
uint32_t* pixels = nullptr;
|
||||
GraphicBufferMapper &mapper = GraphicBufferMapper::get();
|
||||
mapper.lock(mBuffer.memHandle,
|
||||
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_NEVER,
|
||||
android::Rect(mBuffer.width, mBuffer.height),
|
||||
(void **)&pixels);
|
||||
GraphicBufferMapper& mapper = GraphicBufferMapper::get();
|
||||
mapper.lock(mBuffer.memHandle, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_NEVER,
|
||||
android::Rect(mBuffer.width, mBuffer.height), (void**)&pixels);
|
||||
|
||||
// If we failed to lock the pixel buffer, we're about to crash, but log it first
|
||||
if (!pixels) {
|
||||
@@ -286,8 +318,8 @@ Return<EvsResult> EvsDisplay::returnTargetBufferForDisplayImpl(const uint32_t bu
|
||||
// 0xFF in the MSB.
|
||||
// The exception is the very first 32 bits which is used for the
|
||||
// time varying frame signature to avoid getting fooled by a static image.
|
||||
uint32_t expectedPixel = 0xFF0000FF | // MSB and LSB
|
||||
((row & 0xFF) << 8) | // vertical gradient
|
||||
uint32_t expectedPixel = 0xFF0000FF | // MSB and LSB
|
||||
((row & 0xFF) << 8) | // vertical gradient
|
||||
((col & 0xFF) << 16); // horizontal gradient
|
||||
if ((row | col) == 0) {
|
||||
// we'll check the "uniqueness" of the frame signature below
|
||||
@@ -318,39 +350,31 @@ Return<EvsResult> EvsDisplay::returnTargetBufferForDisplayImpl(const uint32_t bu
|
||||
ALOGE("Duplicate, likely stale frame buffer detected");
|
||||
}
|
||||
|
||||
|
||||
// Release our output buffer
|
||||
mapper.unlock(mBuffer.memHandle);
|
||||
|
||||
if (!frameLooksGood) {
|
||||
return EvsResult::UNDERLYING_SERVICE_ERROR;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return EvsResult::OK;
|
||||
}
|
||||
|
||||
|
||||
Return<EvsResult> EvsDisplay::returnTargetBufferForDisplay(const BufferDesc_1_0& buffer) {
|
||||
Return<EvsResult> EvsDisplay::returnTargetBufferForDisplay(const V1_0::BufferDesc& buffer) {
|
||||
return returnTargetBufferForDisplayImpl(buffer.bufferId, buffer.memHandle);
|
||||
}
|
||||
|
||||
|
||||
Return<void> EvsDisplay::getDisplayInfo_1_1(getDisplayInfo_1_1_cb _info_cb) {
|
||||
if (mDisplayProxy != nullptr) {
|
||||
return mDisplayProxy->getDisplayInfo(mDisplayId, _info_cb);
|
||||
} else {
|
||||
HwDisplayConfig nullConfig;
|
||||
HwDisplayState nullState;
|
||||
HwDisplayState nullState;
|
||||
_info_cb(nullConfig, nullState);
|
||||
return Void();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_1
|
||||
} // namespace evs
|
||||
} // namespace automotive
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace android::hardware::automotive::evs::V1_1::implementation
|
||||
|
||||
@@ -17,64 +17,48 @@
|
||||
#ifndef ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSDISPLAY_H
|
||||
#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSDISPLAY_H
|
||||
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsDisplay.h>
|
||||
#include "GlWrapper.h"
|
||||
|
||||
#include <android/frameworks/automotive/display/1.0/IAutomotiveDisplayProxyService.h>
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsDisplay.h>
|
||||
#include <ui/GraphicBuffer.h>
|
||||
|
||||
using ::android::hardware::automotive::evs::V1_1::IEvsDisplay;
|
||||
using ::android::hardware::automotive::evs::V1_0::DisplayDesc;
|
||||
using ::android::hardware::automotive::evs::V1_0::DisplayState;
|
||||
using ::android::hardware::automotive::evs::V1_0::EvsResult;
|
||||
using BufferDesc_1_0 = ::android::hardware::automotive::evs::V1_0::BufferDesc;
|
||||
using android::frameworks::automotive::display::V1_0::IAutomotiveDisplayProxyService;
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace automotive {
|
||||
namespace evs {
|
||||
namespace V1_1 {
|
||||
namespace implementation {
|
||||
|
||||
namespace android::hardware::automotive::evs::V1_1::implementation {
|
||||
|
||||
class EvsDisplay : public IEvsDisplay {
|
||||
public:
|
||||
public:
|
||||
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsDisplay follow.
|
||||
Return<void> getDisplayInfo(getDisplayInfo_cb _hidl_cb) override;
|
||||
Return<EvsResult> setDisplayState(DisplayState state) override;
|
||||
Return<DisplayState> getDisplayState() override;
|
||||
Return<void> getTargetBuffer(getTargetBuffer_cb _hidl_cb) override;
|
||||
Return<EvsResult> returnTargetBufferForDisplay(const BufferDesc_1_0& buffer) override;
|
||||
Return<void> getDisplayInfo(getDisplayInfo_cb _hidl_cb) override;
|
||||
Return<V1_0::EvsResult> setDisplayState(V1_0::DisplayState state) override;
|
||||
Return<V1_0::DisplayState> getDisplayState() override;
|
||||
Return<void> getTargetBuffer(getTargetBuffer_cb _hidl_cb) override;
|
||||
Return<V1_0::EvsResult> returnTargetBufferForDisplay(const V1_0::BufferDesc& buffer) override;
|
||||
|
||||
// Methods from ::android::hardware::automotive::evs::V1_1::IEvsDisplay follow.
|
||||
Return<void> getDisplayInfo_1_1(getDisplayInfo_1_1_cb _info_cb) override;
|
||||
Return<void> getDisplayInfo_1_1(getDisplayInfo_1_1_cb _info_cb) override;
|
||||
|
||||
// Implementation details
|
||||
EvsDisplay();
|
||||
EvsDisplay(sp<IAutomotiveDisplayProxyService> pDisplayProxy, uint64_t displayId);
|
||||
EvsDisplay(
|
||||
sp<frameworks::automotive::display::V1_0::IAutomotiveDisplayProxyService> pDisplayProxy,
|
||||
uint64_t displayId);
|
||||
virtual ~EvsDisplay() override;
|
||||
|
||||
void forceShutdown(); // This gets called if another caller "steals" ownership of the display
|
||||
Return<EvsResult> returnTargetBufferForDisplayImpl(const uint32_t bufferId,
|
||||
const buffer_handle_t memHandle);
|
||||
void forceShutdown(); // This gets called if another caller "steals" ownership of the display
|
||||
Return<V1_0::EvsResult> returnTargetBufferForDisplayImpl(const uint32_t bufferId,
|
||||
const buffer_handle_t memHandle);
|
||||
|
||||
private:
|
||||
DisplayDesc mInfo = {};
|
||||
BufferDesc_1_0 mBuffer = {}; // A graphics buffer into which we'll store images
|
||||
|
||||
bool mFrameBusy = false; // A flag telling us our buffer is in use
|
||||
DisplayState mRequestedState = DisplayState::NOT_VISIBLE;
|
||||
|
||||
std::mutex mAccessLock;
|
||||
|
||||
sp<IAutomotiveDisplayProxyService> mDisplayProxy;
|
||||
uint64_t mDisplayId;
|
||||
private:
|
||||
V1_0::DisplayDesc mInfo = {};
|
||||
V1_0::BufferDesc mBuffer = {}; // A graphics buffer into which we'll store images
|
||||
V1_0::DisplayState mRequestedState = V1_0::DisplayState::NOT_VISIBLE;
|
||||
bool mFrameBusy = false; // A flag telling us our buffer is in use
|
||||
std::mutex mAccessLock;
|
||||
sp<frameworks::automotive::display::V1_0::IAutomotiveDisplayProxyService> mDisplayProxy;
|
||||
uint64_t mDisplayId;
|
||||
std::unique_ptr<GlWrapper> mGlWrapper;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_1
|
||||
} // namespace evs
|
||||
} // namespace automotive
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace android::hardware::automotive::evs::V1_1::implementation
|
||||
|
||||
#endif // ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSDISPLAY_H
|
||||
|
||||
@@ -14,184 +14,171 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "android.hardware.automotive.evs@1.1-service"
|
||||
|
||||
#include "EvsEnumerator.h"
|
||||
#include "EvsCamera.h"
|
||||
#include "EvsDisplay.h"
|
||||
#include "EvsUltrasonicsArray.h"
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace automotive {
|
||||
namespace evs {
|
||||
namespace V1_1 {
|
||||
namespace implementation {
|
||||
using android::frameworks::automotive::display::V1_0::IAutomotiveDisplayProxyService;
|
||||
using android::hardware::automotive::evs::V1_0::EvsResult;
|
||||
|
||||
namespace android::hardware::automotive::evs::V1_1::implementation {
|
||||
|
||||
namespace evs_v1_0 = ::android::hardware::automotive::evs::V1_0;
|
||||
|
||||
// NOTE: All members values are static so that all clients operate on the same state
|
||||
// That is to say, this is effectively a singleton despite the fact that HIDL
|
||||
// constructs a new instance for each client.
|
||||
std::list<EvsEnumerator::CameraRecord> EvsEnumerator::sCameraList;
|
||||
wp<EvsDisplay> EvsEnumerator::sActiveDisplay;
|
||||
unique_ptr<ConfigManager> EvsEnumerator::sConfigManager;
|
||||
sp<IAutomotiveDisplayProxyService> EvsEnumerator::sDisplayProxyService;
|
||||
std::unordered_map<uint8_t, uint64_t> EvsEnumerator::sDisplayPortList;
|
||||
std::list<EvsEnumerator::UltrasonicsArrayRecord> EvsEnumerator::sUltrasonicsArrayRecordList;
|
||||
std::list<EvsEnumerator::CameraRecord> EvsEnumerator::sCameraList;
|
||||
wp<EvsDisplay> EvsEnumerator::sActiveDisplay;
|
||||
std::unique_ptr<ConfigManager> EvsEnumerator::sConfigManager;
|
||||
sp<IAutomotiveDisplayProxyService> EvsEnumerator::sDisplayProxyService;
|
||||
std::unordered_map<uint8_t, uint64_t> EvsEnumerator::sDisplayPortList;
|
||||
std::list<EvsEnumerator::UltrasonicsArrayRecord> EvsEnumerator::sUltrasonicsArrayRecordList;
|
||||
uint64_t EvsEnumerator::sInternalDisplayId;
|
||||
|
||||
EvsEnumerator::EvsEnumerator(sp<IAutomotiveDisplayProxyService> windowService) {
|
||||
ALOGD("EvsEnumerator created");
|
||||
EvsEnumerator::EvsEnumerator(sp<IAutomotiveDisplayProxyService>& windowService) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
// Add sample camera data to our list of cameras
|
||||
// In a real driver, this would be expected to can the available hardware
|
||||
sConfigManager =
|
||||
ConfigManager::Create("/vendor/etc/automotive/evs/evs_default_configuration.xml");
|
||||
ConfigManager::Create("/vendor/etc/automotive/evs/evs_default_configuration.xml");
|
||||
|
||||
// Add available cameras
|
||||
for (auto v : sConfigManager->getCameraList()) {
|
||||
sCameraList.emplace_back(v.c_str());
|
||||
CameraRecord rec(v.data());
|
||||
std::unique_ptr<ConfigManager::CameraInfo>& pInfo = sConfigManager->getCameraInfo(v);
|
||||
if (pInfo) {
|
||||
rec.desc.metadata.setToExternal(reinterpret_cast<uint8_t*>(pInfo->characteristics),
|
||||
get_camera_metadata_size(pInfo->characteristics));
|
||||
}
|
||||
sCameraList.push_back(std::move(rec));
|
||||
}
|
||||
|
||||
if (sDisplayProxyService == nullptr) {
|
||||
if (!sDisplayProxyService) {
|
||||
/* sets a car-window service handle */
|
||||
sDisplayProxyService = windowService;
|
||||
}
|
||||
|
||||
// Add available displays
|
||||
if (sDisplayProxyService != nullptr) {
|
||||
if (sDisplayProxyService) {
|
||||
// Get a display ID list.
|
||||
sDisplayProxyService->getDisplayIdList([](const auto& displayIds) {
|
||||
for (const auto& id : displayIds) {
|
||||
const auto port = id & 0xF;
|
||||
sDisplayPortList.insert_or_assign(port, id);
|
||||
auto status = sDisplayProxyService->getDisplayIdList([](const auto& displayIds) {
|
||||
if (displayIds.size() > 0) {
|
||||
sInternalDisplayId = displayIds[0];
|
||||
for (const auto& id : displayIds) {
|
||||
const auto port = id & 0xF;
|
||||
sDisplayPortList.insert_or_assign(port, id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!status.isOk()) {
|
||||
ALOGE("Failed to read a display list");
|
||||
}
|
||||
}
|
||||
|
||||
// Add ultrasonics array desc.
|
||||
sUltrasonicsArrayRecordList.emplace_back(
|
||||
EvsUltrasonicsArray::GetMockArrayDesc("front_array"));
|
||||
sUltrasonicsArrayRecordList.emplace_back(EvsUltrasonicsArray::GetMockArrayDesc("front_array"));
|
||||
}
|
||||
|
||||
|
||||
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsEnumerator follow.
|
||||
Return<void> EvsEnumerator::getCameraList(getCameraList_cb _hidl_cb) {
|
||||
ALOGD("getCameraList");
|
||||
Return<void> EvsEnumerator::getCameraList(getCameraList_cb _hidl_cb) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
const unsigned numCameras = sCameraList.size();
|
||||
const auto numCameras = sCameraList.size();
|
||||
|
||||
// Build up a packed array of CameraDesc for return
|
||||
// NOTE: Only has to live until the callback returns
|
||||
std::vector<CameraDesc_1_0> descriptions;
|
||||
std::vector<evs_v1_0::CameraDesc> descriptions;
|
||||
descriptions.reserve(numCameras);
|
||||
for (const auto& cam : sCameraList) {
|
||||
descriptions.push_back( cam.desc.v1 );
|
||||
descriptions.push_back(cam.desc.v1);
|
||||
}
|
||||
|
||||
// Encapsulate our camera descriptions in the HIDL vec type
|
||||
hidl_vec<CameraDesc_1_0> hidlCameras(descriptions);
|
||||
hidl_vec<evs_v1_0::CameraDesc> hidlCameras(descriptions);
|
||||
|
||||
// Send back the results
|
||||
ALOGD("reporting %zu cameras available", hidlCameras.size());
|
||||
_hidl_cb(hidlCameras);
|
||||
|
||||
// HIDL convention says we return Void if we sent our result back via callback
|
||||
return Void();
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
Return<sp<IEvsCamera_1_0>> EvsEnumerator::openCamera(const hidl_string& cameraId) {
|
||||
ALOGD("openCamera");
|
||||
Return<sp<evs_v1_0::IEvsCamera>> EvsEnumerator::openCamera(const hidl_string& cameraId) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
// Find the named camera
|
||||
CameraRecord *pRecord = nullptr;
|
||||
for (auto &&cam : sCameraList) {
|
||||
if (cam.desc.v1.cameraId == cameraId) {
|
||||
// Found a match!
|
||||
pRecord = &cam;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Is this a recognized camera id?
|
||||
if (!pRecord) {
|
||||
auto it = std::find_if(sCameraList.begin(), sCameraList.end(), [&cameraId](const auto& cam) {
|
||||
return cameraId == cam.desc.v1.cameraId;
|
||||
});
|
||||
if (it == sCameraList.end()) {
|
||||
ALOGE("Requested camera %s not found", cameraId.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Has this camera already been instantiated by another caller?
|
||||
sp<EvsCamera> pActiveCamera = pRecord->activeInstance.promote();
|
||||
sp<EvsCamera> pActiveCamera = it->activeInstance.promote();
|
||||
if (pActiveCamera != nullptr) {
|
||||
ALOGW("Killing previous camera because of new caller");
|
||||
closeCamera(pActiveCamera);
|
||||
}
|
||||
|
||||
// Construct a camera instance for the caller
|
||||
if (sConfigManager == nullptr) {
|
||||
if (!sConfigManager) {
|
||||
pActiveCamera = EvsCamera::Create(cameraId.c_str());
|
||||
} else {
|
||||
pActiveCamera = EvsCamera::Create(cameraId.c_str(),
|
||||
sConfigManager->getCameraInfo(cameraId));
|
||||
pActiveCamera =
|
||||
EvsCamera::Create(cameraId.c_str(), sConfigManager->getCameraInfo(cameraId));
|
||||
}
|
||||
pRecord->activeInstance = pActiveCamera;
|
||||
if (pActiveCamera == nullptr) {
|
||||
it->activeInstance = pActiveCamera;
|
||||
if (!pActiveCamera) {
|
||||
ALOGE("Failed to allocate new EvsCamera object for %s\n", cameraId.c_str());
|
||||
}
|
||||
|
||||
return pActiveCamera;
|
||||
}
|
||||
|
||||
Return<void> EvsEnumerator::closeCamera(const ::android::sp<evs_v1_0::IEvsCamera>& pCamera) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
Return<void> EvsEnumerator::closeCamera(const ::android::sp<IEvsCamera_1_0>& pCamera) {
|
||||
ALOGD("closeCamera");
|
||||
|
||||
auto pCamera_1_1 = IEvsCamera_1_1::castFrom(pCamera).withDefault(nullptr);
|
||||
if (pCamera_1_1 == nullptr) {
|
||||
auto pCamera_1_1 = IEvsCamera::castFrom(pCamera).withDefault(nullptr);
|
||||
if (!pCamera_1_1) {
|
||||
ALOGE("Ignoring call to closeCamera with null camera ptr");
|
||||
return Void();
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get the camera id so we can find it in our list
|
||||
std::string cameraId;
|
||||
pCamera_1_1->getCameraInfo_1_1([&cameraId](CameraDesc desc) {
|
||||
cameraId = desc.v1.cameraId;
|
||||
}
|
||||
);
|
||||
pCamera_1_1->getCameraInfo_1_1([&cameraId](CameraDesc desc) { cameraId = desc.v1.cameraId; });
|
||||
|
||||
// Find the named camera
|
||||
CameraRecord *pRecord = nullptr;
|
||||
for (auto &&cam : sCameraList) {
|
||||
if (cam.desc.v1.cameraId == cameraId) {
|
||||
// Found a match!
|
||||
pRecord = &cam;
|
||||
break;
|
||||
}
|
||||
auto it = std::find_if(sCameraList.begin(), sCameraList.end(), [&cameraId](const auto& cam) {
|
||||
return cameraId == cam.desc.v1.cameraId;
|
||||
});
|
||||
if (it == sCameraList.end()) {
|
||||
ALOGE("Ignores a request to close unknown camera, %s", cameraId.data());
|
||||
return {};
|
||||
}
|
||||
|
||||
// Is the display being destroyed actually the one we think is active?
|
||||
if (!pRecord) {
|
||||
ALOGE("Asked to close a camera who's name isn't recognized");
|
||||
sp<EvsCamera> pActiveCamera = it->activeInstance.promote();
|
||||
if (!pActiveCamera) {
|
||||
ALOGE("Somehow a camera is being destroyed when the enumerator didn't know one existed");
|
||||
} else if (pActiveCamera != pCamera_1_1) {
|
||||
// This can happen if the camera was aggressively reopened, orphaning this previous instance
|
||||
ALOGW("Ignoring close of previously orphaned camera - why did a client steal?");
|
||||
} else {
|
||||
sp<EvsCamera> pActiveCamera = pRecord->activeInstance.promote();
|
||||
|
||||
if (pActiveCamera == nullptr) {
|
||||
ALOGE("Somehow a camera is being destroyed when the enumerator didn't know one existed");
|
||||
} else if (pActiveCamera != pCamera_1_1) {
|
||||
// This can happen if the camera was aggressively reopened, orphaning this previous instance
|
||||
ALOGW("Ignoring close of previously orphaned camera - why did a client steal?");
|
||||
} else {
|
||||
// Drop the active camera
|
||||
pActiveCamera->forceShutdown();
|
||||
pRecord->activeInstance = nullptr;
|
||||
}
|
||||
// Drop the active camera
|
||||
pActiveCamera->forceShutdown();
|
||||
it->activeInstance = nullptr;
|
||||
}
|
||||
|
||||
return Void();
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
Return<sp<IEvsDisplay_1_0>> EvsEnumerator::openDisplay() {
|
||||
ALOGD("openDisplay");
|
||||
Return<sp<V1_0::IEvsDisplay>> EvsEnumerator::openDisplay() {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
// If we already have a display active, then we need to shut it down so we can
|
||||
// give exclusive access to the new caller.
|
||||
@@ -202,28 +189,25 @@ Return<sp<IEvsDisplay_1_0>> EvsEnumerator::openDisplay() {
|
||||
}
|
||||
|
||||
// Create a new display interface and return it
|
||||
pActiveDisplay = new EvsDisplay();
|
||||
pActiveDisplay = new EvsDisplay(sDisplayProxyService, sInternalDisplayId);
|
||||
sActiveDisplay = pActiveDisplay;
|
||||
|
||||
ALOGD("Returning new EvsDisplay object %p", pActiveDisplay.get());
|
||||
return pActiveDisplay;
|
||||
}
|
||||
|
||||
|
||||
Return<void> EvsEnumerator::getDisplayIdList(getDisplayIdList_cb _list_cb) {
|
||||
hidl_vec<uint8_t> ids;
|
||||
|
||||
ids.resize(sDisplayPortList.size());
|
||||
|
||||
unsigned i = 0;
|
||||
for (const auto& [port, id] : sDisplayPortList) {
|
||||
ids[i++] = port;
|
||||
}
|
||||
std::for_each(sDisplayPortList.begin(), sDisplayPortList.end(),
|
||||
[&](const auto& element) { ids[i++] = element.first; });
|
||||
|
||||
_list_cb(ids);
|
||||
return Void();
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
Return<sp<IEvsDisplay>> EvsEnumerator::openDisplay_1_1(uint8_t port) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
@@ -243,10 +227,8 @@ Return<sp<IEvsDisplay>> EvsEnumerator::openDisplay_1_1(uint8_t port) {
|
||||
return pActiveDisplay;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Return<void> EvsEnumerator::closeDisplay(const ::android::sp<IEvsDisplay_1_0>& pDisplay) {
|
||||
ALOGD("closeDisplay");
|
||||
Return<void> EvsEnumerator::closeDisplay(const ::android::sp<V1_0::IEvsDisplay>& pDisplay) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
// Do we still have a display object we think should be active?
|
||||
sp<EvsDisplay> pActiveDisplay = sActiveDisplay.promote();
|
||||
@@ -260,123 +242,111 @@ Return<void> EvsEnumerator::closeDisplay(const ::android::sp<IEvsDisplay_1_0>& p
|
||||
sActiveDisplay = nullptr;
|
||||
}
|
||||
|
||||
return Void();
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
Return<DisplayState> EvsEnumerator::getDisplayState() {
|
||||
ALOGD("getDisplayState");
|
||||
Return<V1_0::DisplayState> EvsEnumerator::getDisplayState() {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
// Do we still have a display object we think should be active?
|
||||
sp<IEvsDisplay> pActiveDisplay = sActiveDisplay.promote();
|
||||
if (pActiveDisplay != nullptr) {
|
||||
return pActiveDisplay->getDisplayState();
|
||||
} else {
|
||||
return DisplayState::NOT_OPEN;
|
||||
return V1_0::DisplayState::NOT_OPEN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Methods from ::android::hardware::automotive::evs::V1_1::IEvsEnumerator follow.
|
||||
Return<void> EvsEnumerator::getCameraList_1_1(getCameraList_1_1_cb _hidl_cb) {
|
||||
ALOGD("getCameraList");
|
||||
Return<void> EvsEnumerator::getCameraList_1_1(getCameraList_1_1_cb _hidl_cb) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
const unsigned numCameras = sCameraList.size();
|
||||
const auto numCameras = sCameraList.size();
|
||||
|
||||
// Build up a packed array of CameraDesc for return
|
||||
// NOTE: Only has to live until the callback returns
|
||||
std::vector<CameraDesc_1_1> descriptions;
|
||||
std::vector<CameraDesc> descriptions;
|
||||
descriptions.reserve(numCameras);
|
||||
for (const auto& cam : sCameraList) {
|
||||
descriptions.push_back( cam.desc );
|
||||
}
|
||||
std::for_each(sCameraList.begin(), sCameraList.end(),
|
||||
[&](const auto& cam) { descriptions.push_back(cam.desc); });
|
||||
|
||||
// Encapsulate our camera descriptions in the HIDL vec type
|
||||
hidl_vec<CameraDesc_1_1> hidlCameras(descriptions);
|
||||
hidl_vec<CameraDesc> hidlCameras(descriptions);
|
||||
|
||||
// Send back the results
|
||||
ALOGD("reporting %zu cameras available", hidlCameras.size());
|
||||
_hidl_cb(hidlCameras);
|
||||
|
||||
// HIDL convention says we return Void if we sent our result back via callback
|
||||
return Void();
|
||||
return {};
|
||||
}
|
||||
|
||||
Return<sp<IEvsCamera_1_1>>
|
||||
EvsEnumerator::openCamera_1_1(const hidl_string& cameraId,
|
||||
const Stream& streamCfg) {
|
||||
// Find the named camera
|
||||
CameraRecord *pRecord = nullptr;
|
||||
for (auto &&cam : sCameraList) {
|
||||
if (cam.desc.v1.cameraId == cameraId) {
|
||||
// Found a match!
|
||||
pRecord = &cam;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Return<sp<IEvsCamera>> EvsEnumerator::openCamera_1_1(const hidl_string& cameraId,
|
||||
const Stream& streamCfg) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
// Is this a recognized camera id?
|
||||
if (!pRecord) {
|
||||
// Find the named camera
|
||||
auto it = std::find_if(sCameraList.begin(), sCameraList.end(), [&cameraId](const auto& cam) {
|
||||
return cameraId == cam.desc.v1.cameraId;
|
||||
});
|
||||
if (it == sCameraList.end()) {
|
||||
ALOGE("Requested camera %s not found", cameraId.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Has this camera already been instantiated by another caller?
|
||||
sp<EvsCamera> pActiveCamera = pRecord->activeInstance.promote();
|
||||
sp<EvsCamera> pActiveCamera = it->activeInstance.promote();
|
||||
if (pActiveCamera != nullptr) {
|
||||
ALOGW("Killing previous camera because of new caller");
|
||||
closeCamera(pActiveCamera);
|
||||
}
|
||||
|
||||
// Construct a camera instance for the caller
|
||||
if (sConfigManager == nullptr) {
|
||||
if (!sConfigManager) {
|
||||
pActiveCamera = EvsCamera::Create(cameraId.c_str());
|
||||
} else {
|
||||
pActiveCamera = EvsCamera::Create(cameraId.c_str(),
|
||||
sConfigManager->getCameraInfo(cameraId),
|
||||
pActiveCamera = EvsCamera::Create(cameraId.c_str(), sConfigManager->getCameraInfo(cameraId),
|
||||
&streamCfg);
|
||||
}
|
||||
|
||||
pRecord->activeInstance = pActiveCamera;
|
||||
if (pActiveCamera == nullptr) {
|
||||
it->activeInstance = pActiveCamera;
|
||||
if (!pActiveCamera) {
|
||||
ALOGE("Failed to allocate new EvsCamera object for %s\n", cameraId.c_str());
|
||||
}
|
||||
|
||||
return pActiveCamera;
|
||||
}
|
||||
|
||||
|
||||
EvsEnumerator::CameraRecord* EvsEnumerator::findCameraById(const std::string& cameraId) {
|
||||
// Find the named camera
|
||||
CameraRecord *pRecord = nullptr;
|
||||
for (auto &&cam : sCameraList) {
|
||||
if (cam.desc.v1.cameraId == cameraId) {
|
||||
// Found a match!
|
||||
pRecord = &cam;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
return pRecord;
|
||||
// Find the named camera
|
||||
auto it = std::find_if(sCameraList.begin(), sCameraList.end(), [&cameraId](const auto& cam) {
|
||||
return cameraId == cam.desc.v1.cameraId;
|
||||
});
|
||||
return (it != sCameraList.end()) ? &*it : nullptr;
|
||||
}
|
||||
|
||||
EvsEnumerator::UltrasonicsArrayRecord* EvsEnumerator::findUltrasonicsArrayById(
|
||||
const std::string& ultrasonicsArrayId) {
|
||||
auto recordIt = std::find_if(
|
||||
sUltrasonicsArrayRecordList.begin(), sUltrasonicsArrayRecordList.end(),
|
||||
[&ultrasonicsArrayId](const UltrasonicsArrayRecord& record) {
|
||||
return ultrasonicsArrayId == record.desc.ultrasonicsArrayId;});
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
auto recordIt =
|
||||
std::find_if(sUltrasonicsArrayRecordList.begin(), sUltrasonicsArrayRecordList.end(),
|
||||
[&ultrasonicsArrayId](const UltrasonicsArrayRecord& record) {
|
||||
return ultrasonicsArrayId == record.desc.ultrasonicsArrayId;
|
||||
});
|
||||
|
||||
return (recordIt != sUltrasonicsArrayRecordList.end()) ? &*recordIt : nullptr;
|
||||
}
|
||||
|
||||
Return<void> EvsEnumerator::getUltrasonicsArrayList(getUltrasonicsArrayList_cb _hidl_cb) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
hidl_vec<UltrasonicsArrayDesc> desc;
|
||||
desc.resize(sUltrasonicsArrayRecordList.size());
|
||||
|
||||
// Copy over desc from sUltrasonicsArrayRecordList.
|
||||
for (auto p = std::make_pair(sUltrasonicsArrayRecordList.begin(), desc.begin());
|
||||
p.first != sUltrasonicsArrayRecordList.end(); p.first++, p.second++) {
|
||||
p.first != sUltrasonicsArrayRecordList.end(); p.first++, p.second++) {
|
||||
*p.second = p.first->desc;
|
||||
}
|
||||
|
||||
@@ -385,11 +355,13 @@ Return<void> EvsEnumerator::getUltrasonicsArrayList(getUltrasonicsArrayList_cb _
|
||||
_hidl_cb(desc);
|
||||
|
||||
// HIDL convention says we return Void if we sent our result back via callback
|
||||
return Void();
|
||||
return {};
|
||||
}
|
||||
|
||||
Return<sp<IEvsUltrasonicsArray>> EvsEnumerator::openUltrasonicsArray(
|
||||
const hidl_string& ultrasonicsArrayId) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
// Find the named ultrasonic array.
|
||||
UltrasonicsArrayRecord* pRecord = findUltrasonicsArrayById(ultrasonicsArrayId);
|
||||
|
||||
@@ -419,10 +391,11 @@ Return<sp<IEvsUltrasonicsArray>> EvsEnumerator::openUltrasonicsArray(
|
||||
|
||||
Return<void> EvsEnumerator::closeUltrasonicsArray(
|
||||
const sp<IEvsUltrasonicsArray>& pEvsUltrasonicsArray) {
|
||||
ALOGD("%s", __FUNCTION__);
|
||||
|
||||
if (pEvsUltrasonicsArray.get() == nullptr) {
|
||||
ALOGE("Ignoring call to closeUltrasonicsArray with null ultrasonics array");
|
||||
return Void();
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get the ultrasonics array id so we can find it in our list.
|
||||
@@ -435,7 +408,7 @@ Return<void> EvsEnumerator::closeUltrasonicsArray(
|
||||
UltrasonicsArrayRecord* pRecord = findUltrasonicsArrayById(ultrasonicsArrayId);
|
||||
if (!pRecord) {
|
||||
ALOGE("Asked to close a ultrasonics array whose name isnt not found");
|
||||
return Void();
|
||||
return {};
|
||||
}
|
||||
|
||||
sp<EvsUltrasonicsArray> pActiveUltrasonicsArray = pRecord->activeInstance.promote();
|
||||
@@ -453,12 +426,7 @@ Return<void> EvsEnumerator::closeUltrasonicsArray(
|
||||
pRecord->activeInstance = nullptr;
|
||||
}
|
||||
|
||||
return Void();
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_1
|
||||
} // namespace evs
|
||||
} // namespace automotive
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace android::hardware::automotive::evs::V1_1::implementation
|
||||
|
||||
@@ -17,56 +17,41 @@
|
||||
#ifndef ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSCAMERAENUMERATOR_H
|
||||
#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSCAMERAENUMERATOR_H
|
||||
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsEnumerator.h>
|
||||
#include "ConfigManager.h"
|
||||
|
||||
#include <android/frameworks/automotive/display/1.0/IAutomotiveDisplayProxyService.h>
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsCamera.h>
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsDisplay.h>
|
||||
#include <android/frameworks/automotive/display/1.0/IAutomotiveDisplayProxyService.h>
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsEnumerator.h>
|
||||
#include <android/hardware/automotive/evs/1.1/IEvsUltrasonicsArray.h>
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "ConfigManager.h"
|
||||
namespace android::hardware::automotive::evs::V1_1::implementation {
|
||||
|
||||
using ::android::hardware::automotive::evs::V1_0::EvsResult;
|
||||
using ::android::hardware::automotive::evs::V1_0::DisplayState;
|
||||
using IEvsCamera_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsCamera;
|
||||
using IEvsCamera_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsCamera;
|
||||
using CameraDesc_1_0 = ::android::hardware::automotive::evs::V1_0::CameraDesc;
|
||||
using CameraDesc_1_1 = ::android::hardware::automotive::evs::V1_1::CameraDesc;
|
||||
using IEvsDisplay_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsDisplay;
|
||||
using IEvsDisplay_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsDisplay;
|
||||
using android::frameworks::automotive::display::V1_0::IAutomotiveDisplayProxyService;
|
||||
namespace evs_v1_0 = ::android::hardware::automotive::evs::V1_0;
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace automotive {
|
||||
namespace evs {
|
||||
namespace V1_1 {
|
||||
namespace implementation {
|
||||
|
||||
|
||||
class EvsCamera; // from EvsCamera.h
|
||||
class EvsDisplay; // from EvsDisplay.h
|
||||
class EvsCamera; // from EvsCamera.h
|
||||
class EvsDisplay; // from EvsDisplay.h
|
||||
class EvsUltrasonicsArray; // from EvsUltrasonicsArray.h
|
||||
|
||||
|
||||
class EvsEnumerator : public IEvsEnumerator {
|
||||
public:
|
||||
public:
|
||||
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsEnumerator follow.
|
||||
Return<void> getCameraList(getCameraList_cb _hidl_cb) override;
|
||||
Return<sp<IEvsCamera_1_0>> openCamera(const hidl_string& cameraId) override;
|
||||
Return<void> closeCamera(const ::android::sp<IEvsCamera_1_0>& carCamera) override;
|
||||
Return<sp<IEvsDisplay_1_0>> openDisplay() override;
|
||||
Return<void> closeDisplay(const ::android::sp<IEvsDisplay_1_0>& display) override;
|
||||
Return<DisplayState> getDisplayState() override;
|
||||
Return<void> getCameraList(getCameraList_cb _hidl_cb) override;
|
||||
Return<sp<evs_v1_0::IEvsCamera>> openCamera(const hidl_string& cameraId) override;
|
||||
Return<void> closeCamera(const ::android::sp<evs_v1_0::IEvsCamera>& carCamera) override;
|
||||
Return<sp<evs_v1_0::IEvsDisplay>> openDisplay() override;
|
||||
Return<void> closeDisplay(const ::android::sp<evs_v1_0::IEvsDisplay>& display) override;
|
||||
Return<V1_0::DisplayState> getDisplayState() override;
|
||||
|
||||
// Methods from ::android::hardware::automotive::evs::V1_1::IEvsEnumerator follow.
|
||||
Return<void> getCameraList_1_1(getCameraList_1_1_cb _hidl_cb) override;
|
||||
Return<sp<IEvsCamera_1_1>> openCamera_1_1(const hidl_string& cameraId,
|
||||
const Stream& streamCfg) override;
|
||||
Return<void> getCameraList_1_1(getCameraList_1_1_cb _hidl_cb) override;
|
||||
Return<sp<IEvsCamera>> openCamera_1_1(const hidl_string& cameraId,
|
||||
const Stream& streamCfg) override;
|
||||
Return<bool> isHardware() override { return true; }
|
||||
Return<void> getDisplayIdList(getDisplayIdList_cb _list_cb) override;
|
||||
Return<sp<IEvsDisplay_1_1>> openDisplay_1_1(uint8_t port) override;
|
||||
Return<void> getDisplayIdList(getDisplayIdList_cb _list_cb) override;
|
||||
Return<sp<IEvsDisplay>> openDisplay_1_1(uint8_t port) override;
|
||||
Return<void> getUltrasonicsArrayList(getUltrasonicsArrayList_cb _hidl_cb) override;
|
||||
Return<sp<IEvsUltrasonicsArray>> openUltrasonicsArray(
|
||||
const hidl_string& ultrasonicsArrayId) override;
|
||||
@@ -74,49 +59,40 @@ public:
|
||||
const ::android::sp<IEvsUltrasonicsArray>& evsUltrasonicsArray) override;
|
||||
|
||||
// Implementation details
|
||||
EvsEnumerator(sp<IAutomotiveDisplayProxyService> windowService = nullptr);
|
||||
EvsEnumerator(sp<frameworks::automotive::display::V1_0::IAutomotiveDisplayProxyService>&
|
||||
windowService);
|
||||
|
||||
private:
|
||||
private:
|
||||
// NOTE: All members values are static so that all clients operate on the same state
|
||||
// That is to say, this is effectively a singleton despite the fact that HIDL
|
||||
// constructs a new instance for each client.
|
||||
struct CameraRecord {
|
||||
CameraDesc_1_1 desc;
|
||||
wp<EvsCamera> activeInstance;
|
||||
CameraDesc desc;
|
||||
wp<EvsCamera> activeInstance;
|
||||
|
||||
CameraRecord(const char *cameraId) : desc() { desc.v1.cameraId = cameraId; }
|
||||
CameraRecord(const char* cameraId) : desc() { desc.v1.cameraId = cameraId; }
|
||||
};
|
||||
|
||||
struct UltrasonicsArrayRecord {
|
||||
UltrasonicsArrayDesc desc;
|
||||
wp<EvsUltrasonicsArray> activeInstance;
|
||||
|
||||
UltrasonicsArrayRecord(const UltrasonicsArrayDesc& arrayDesc) : desc(arrayDesc) {};
|
||||
UltrasonicsArrayRecord(const UltrasonicsArrayDesc& arrayDesc) : desc(arrayDesc){};
|
||||
};
|
||||
|
||||
static CameraRecord* findCameraById(const std::string& cameraId);
|
||||
|
||||
static std::list<CameraRecord> sCameraList;
|
||||
|
||||
static std::list<CameraRecord> sCameraList;
|
||||
static UltrasonicsArrayRecord* findUltrasonicsArrayById(const std::string& ultrasonicsArrayId);
|
||||
|
||||
static std::list<UltrasonicsArrayRecord> sUltrasonicsArrayRecordList;
|
||||
|
||||
// Weak pointer. Object destructs if client dies.
|
||||
static wp<EvsDisplay> sActiveDisplay;
|
||||
|
||||
static unique_ptr<ConfigManager> sConfigManager;
|
||||
|
||||
static sp<IAutomotiveDisplayProxyService> sDisplayProxyService;
|
||||
static std::unordered_map<uint8_t,
|
||||
uint64_t> sDisplayPortList;
|
||||
static wp<EvsDisplay> sActiveDisplay; // Weak pointer. Object destructs if client dies.
|
||||
static uint64_t sInternalDisplayId;
|
||||
static sp<frameworks::automotive::display::V1_0::IAutomotiveDisplayProxyService>
|
||||
sDisplayProxyService;
|
||||
static std::unordered_map<uint8_t, uint64_t> sDisplayPortList;
|
||||
static std::unique_ptr<ConfigManager> sConfigManager;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_1
|
||||
} // namespace evs
|
||||
} // namespace automotive
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace android::hardware::automotive::evs::V1_1::implementation
|
||||
|
||||
#endif // ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSCAMERAENUMERATOR_H
|
||||
|
||||
@@ -113,10 +113,9 @@ bool fillMockDataFrame(UltrasonicsDataFrameDesc& dataFrameDesc, sp<IMemory> pIMe
|
||||
dataFrameDesc.receiversReadingsCountList = receiversReadingsCountList;
|
||||
|
||||
const std::vector<WaveformData> waveformDataList = {
|
||||
{recvIdList[0], { {1000, 0.1f}, {2000, 0.8f} }},
|
||||
{recvIdList[1], { {1000, 0.1f}, {2000, 1.0f} }},
|
||||
{recvIdList[2], { {1000, 0.1f}, {2000, 0.2f}, {4000, 0.2f}, {5000, 0.1f} }}
|
||||
};
|
||||
{recvIdList[0], {{1000, 0.1f}, {2000, 0.8f}}},
|
||||
{recvIdList[1], {{1000, 0.1f}, {2000, 1.0f}}},
|
||||
{recvIdList[2], {{1000, 0.1f}, {2000, 0.2f}, {4000, 0.2f}, {5000, 0.1f}}}};
|
||||
|
||||
if (pIMemory.get() == nullptr) {
|
||||
return false;
|
||||
|
||||
@@ -119,7 +119,7 @@ class EvsUltrasonicsArray : public IEvsUltrasonicsArray {
|
||||
std::mutex mAccessLock;
|
||||
std::vector<DataFrameRecord> mDataFrames GUARDED_BY(mAccessLock); // Shared memory buffers.
|
||||
unsigned mFramesAllowed GUARDED_BY(mAccessLock); // How many buffers are we currently using.
|
||||
unsigned mFramesInUse GUARDED_BY(mAccessLock); // How many buffers are currently outstanding.
|
||||
unsigned mFramesInUse GUARDED_BY(mAccessLock); // How many buffers are currently outstanding.
|
||||
|
||||
StreamStateValues mStreamState GUARDED_BY(mAccessLock);
|
||||
};
|
||||
|
||||
450
automotive/evs/1.1/default/GlWrapper.cpp
Normal file
450
automotive/evs/1.1/default/GlWrapper.cpp
Normal file
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 "GlWrapper.h"
|
||||
|
||||
#include <ui/DisplayMode.h>
|
||||
#include <ui/DisplayState.h>
|
||||
#include <ui/GraphicBuffer.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <utility>
|
||||
|
||||
using android::GraphicBuffer;
|
||||
using android::sp;
|
||||
|
||||
namespace {
|
||||
|
||||
// Defines a default color to clear the screen in RGBA format
|
||||
constexpr float kDefaultColorInRgba[] = {0.1f, 0.5f, 0.1f, 1.0f};
|
||||
|
||||
// Defines the size of the preview area relative to the entire display
|
||||
constexpr float kDisplayAreaRatio = 0.8f;
|
||||
|
||||
constexpr const char vertexShaderSource[] =
|
||||
""
|
||||
"#version 300 es \n"
|
||||
"layout(location = 0) in vec4 pos; \n"
|
||||
"layout(location = 1) in vec2 tex; \n"
|
||||
"out vec2 uv; \n"
|
||||
"void main() \n"
|
||||
"{ \n"
|
||||
" gl_Position = pos; \n"
|
||||
" uv = tex; \n"
|
||||
"} \n";
|
||||
|
||||
constexpr const char pixelShaderSource[] =
|
||||
"#version 300 es \n"
|
||||
"precision mediump float; \n"
|
||||
"uniform sampler2D tex; \n"
|
||||
"in vec2 uv; \n"
|
||||
"out vec4 color; \n"
|
||||
"void main() \n"
|
||||
"{ \n"
|
||||
" vec4 texel = texture(tex, uv); \n"
|
||||
" color = texel; \n"
|
||||
"} \n";
|
||||
|
||||
const char* getEGLError(void) {
|
||||
switch (eglGetError()) {
|
||||
case EGL_SUCCESS:
|
||||
return "EGL_SUCCESS";
|
||||
case EGL_NOT_INITIALIZED:
|
||||
return "EGL_NOT_INITIALIZED";
|
||||
case EGL_BAD_ACCESS:
|
||||
return "EGL_BAD_ACCESS";
|
||||
case EGL_BAD_ALLOC:
|
||||
return "EGL_BAD_ALLOC";
|
||||
case EGL_BAD_ATTRIBUTE:
|
||||
return "EGL_BAD_ATTRIBUTE";
|
||||
case EGL_BAD_CONTEXT:
|
||||
return "EGL_BAD_CONTEXT";
|
||||
case EGL_BAD_CONFIG:
|
||||
return "EGL_BAD_CONFIG";
|
||||
case EGL_BAD_CURRENT_SURFACE:
|
||||
return "EGL_BAD_CURRENT_SURFACE";
|
||||
case EGL_BAD_DISPLAY:
|
||||
return "EGL_BAD_DISPLAY";
|
||||
case EGL_BAD_SURFACE:
|
||||
return "EGL_BAD_SURFACE";
|
||||
case EGL_BAD_MATCH:
|
||||
return "EGL_BAD_MATCH";
|
||||
case EGL_BAD_PARAMETER:
|
||||
return "EGL_BAD_PARAMETER";
|
||||
case EGL_BAD_NATIVE_PIXMAP:
|
||||
return "EGL_BAD_NATIVE_PIXMAP";
|
||||
case EGL_BAD_NATIVE_WINDOW:
|
||||
return "EGL_BAD_NATIVE_WINDOW";
|
||||
case EGL_CONTEXT_LOST:
|
||||
return "EGL_CONTEXT_LOST";
|
||||
default:
|
||||
return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
// Given shader source, load and compile it
|
||||
GLuint loadShader(GLenum type, const char* shaderSrc) {
|
||||
// Create the shader object
|
||||
GLuint shader = glCreateShader(type);
|
||||
if (shader == 0) {
|
||||
LOG(ERROR) << "glCreateSharder() failed with error = " << glGetError();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Load and compile the shader
|
||||
glShaderSource(shader, 1, &shaderSrc, nullptr);
|
||||
glCompileShader(shader);
|
||||
|
||||
// Verify the compilation worked as expected
|
||||
GLint compiled = 0;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
|
||||
if (!compiled) {
|
||||
LOG(ERROR) << "Error compiling shader";
|
||||
|
||||
GLint size = 0;
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &size);
|
||||
if (size > 0) {
|
||||
// Get and report the error message
|
||||
char infoLog[size];
|
||||
glGetShaderInfoLog(shader, size, nullptr, infoLog);
|
||||
LOG(ERROR) << " msg:" << std::endl << infoLog;
|
||||
}
|
||||
|
||||
glDeleteShader(shader);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
// Create a program object given vertex and pixels shader source
|
||||
GLuint buildShaderProgram(const char* vtxSrc, const char* pxlSrc) {
|
||||
GLuint program = glCreateProgram();
|
||||
if (program == 0) {
|
||||
LOG(ERROR) << "Failed to allocate program object";
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Compile the shaders and bind them to this program
|
||||
GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vtxSrc);
|
||||
if (vertexShader == 0) {
|
||||
LOG(ERROR) << "Failed to load vertex shader";
|
||||
glDeleteProgram(program);
|
||||
return 0;
|
||||
}
|
||||
GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pxlSrc);
|
||||
if (pixelShader == 0) {
|
||||
LOG(ERROR) << "Failed to load pixel shader";
|
||||
glDeleteProgram(program);
|
||||
glDeleteShader(vertexShader);
|
||||
return 0;
|
||||
}
|
||||
glAttachShader(program, vertexShader);
|
||||
glAttachShader(program, pixelShader);
|
||||
|
||||
// Link the program
|
||||
glLinkProgram(program);
|
||||
GLint linked = 0;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &linked);
|
||||
if (!linked) {
|
||||
LOG(ERROR) << "Error linking program";
|
||||
GLint size = 0;
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &size);
|
||||
if (size > 0) {
|
||||
// Get and report the error message
|
||||
char* infoLog = (char*)malloc(size);
|
||||
glGetProgramInfoLog(program, size, nullptr, infoLog);
|
||||
LOG(ERROR) << " msg: " << infoLog;
|
||||
free(infoLog);
|
||||
}
|
||||
|
||||
glDeleteProgram(program);
|
||||
glDeleteShader(vertexShader);
|
||||
glDeleteShader(pixelShader);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace android::hardware::automotive::evs::V1_1::implementation {
|
||||
|
||||
// Main entry point
|
||||
bool GlWrapper::initialize(const sp<IAutomotiveDisplayProxyService>& service, uint64_t displayId) {
|
||||
LOG(DEBUG) << __FUNCTION__;
|
||||
|
||||
if (!service) {
|
||||
LOG(WARNING) << "IAutomotiveDisplayProxyService is invalid.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// We will use the first display in the list as the primary.
|
||||
service->getDisplayInfo(displayId, [this](auto dpyConfig, auto dpyState) {
|
||||
ui::DisplayMode* pConfig = reinterpret_cast<ui::DisplayMode*>(dpyConfig.data());
|
||||
mWidth = pConfig->resolution.getWidth();
|
||||
mHeight = pConfig->resolution.getHeight();
|
||||
|
||||
ui::DisplayState* pState = reinterpret_cast<ui::DisplayState*>(dpyState.data());
|
||||
if (pState->orientation != ui::ROTATION_0 && pState->orientation != ui::ROTATION_180) {
|
||||
// rotate
|
||||
std::swap(mWidth, mHeight);
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "Display resolution is " << mWidth << " x " << mHeight;
|
||||
});
|
||||
|
||||
mGfxBufferProducer = service->getIGraphicBufferProducer(displayId);
|
||||
if (mGfxBufferProducer == nullptr) {
|
||||
LOG(ERROR) << "Failed to get IGraphicBufferProducer from IAutomotiveDisplayProxyService.";
|
||||
return false;
|
||||
}
|
||||
|
||||
mSurfaceHolder = getSurfaceFromHGBP(mGfxBufferProducer);
|
||||
if (mSurfaceHolder == nullptr) {
|
||||
LOG(ERROR) << "Failed to get a Surface from HGBP.";
|
||||
return false;
|
||||
}
|
||||
|
||||
mWindow = getNativeWindow(mSurfaceHolder.get());
|
||||
if (mWindow == nullptr) {
|
||||
LOG(ERROR) << "Failed to get a native window from Surface.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set up our OpenGL ES context associated with the default display
|
||||
mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
if (mDisplay == EGL_NO_DISPLAY) {
|
||||
LOG(ERROR) << "Failed to get egl display";
|
||||
return false;
|
||||
}
|
||||
|
||||
EGLint major = 3;
|
||||
EGLint minor = 0;
|
||||
if (!eglInitialize(mDisplay, &major, &minor)) {
|
||||
LOG(ERROR) << "Failed to initialize EGL: " << getEGLError();
|
||||
return false;
|
||||
}
|
||||
|
||||
const EGLint config_attribs[] = {
|
||||
// Tag Value
|
||||
EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_DEPTH_SIZE, 0, EGL_NONE};
|
||||
|
||||
// Pick the default configuration without constraints (is this good enough?)
|
||||
EGLConfig egl_config = {0};
|
||||
EGLint numConfigs = -1;
|
||||
eglChooseConfig(mDisplay, config_attribs, &egl_config, 1, &numConfigs);
|
||||
if (numConfigs != 1) {
|
||||
LOG(ERROR) << "Didn't find a suitable format for our display window";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the EGL render target surface
|
||||
mSurface = eglCreateWindowSurface(mDisplay, egl_config, mWindow, nullptr);
|
||||
if (mSurface == EGL_NO_SURFACE) {
|
||||
LOG(ERROR) << "eglCreateWindowSurface failed: " << getEGLError();
|
||||
;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the EGL context
|
||||
// NOTE: Our shader is (currently at least) written to require version 3, so this
|
||||
// is required.
|
||||
const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
|
||||
mContext = eglCreateContext(mDisplay, egl_config, EGL_NO_CONTEXT, context_attribs);
|
||||
if (mContext == EGL_NO_CONTEXT) {
|
||||
LOG(ERROR) << "Failed to create OpenGL ES Context: " << getEGLError();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Activate our render target for drawing
|
||||
if (!eglMakeCurrent(mDisplay, mSurface, mSurface, mContext)) {
|
||||
LOG(ERROR) << "Failed to make the OpenGL ES Context current: " << getEGLError();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the shader program for our simple pipeline
|
||||
mShaderProgram = buildShaderProgram(vertexShaderSource, pixelShaderSource);
|
||||
if (!mShaderProgram) {
|
||||
LOG(ERROR) << "Failed to build shader program: " << getEGLError();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a GL texture that will eventually wrap our externally created texture surface(s)
|
||||
glGenTextures(1, &mTextureMap);
|
||||
if (mTextureMap <= 0) {
|
||||
LOG(ERROR) << "Didn't get a texture handle allocated: " << getEGLError();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Turn off mip-mapping for the created texture surface
|
||||
// (the inbound camera imagery doesn't have MIPs)
|
||||
glBindTexture(GL_TEXTURE_2D, mTextureMap);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GlWrapper::shutdown() {
|
||||
// Drop our device textures
|
||||
if (mKHRimage != EGL_NO_IMAGE_KHR) {
|
||||
eglDestroyImageKHR(mDisplay, mKHRimage);
|
||||
mKHRimage = EGL_NO_IMAGE_KHR;
|
||||
}
|
||||
|
||||
// Release all GL resources
|
||||
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
eglDestroySurface(mDisplay, mSurface);
|
||||
eglDestroyContext(mDisplay, mContext);
|
||||
eglTerminate(mDisplay);
|
||||
mSurface = EGL_NO_SURFACE;
|
||||
mContext = EGL_NO_CONTEXT;
|
||||
mDisplay = EGL_NO_DISPLAY;
|
||||
|
||||
// Release the window
|
||||
mSurfaceHolder = nullptr;
|
||||
}
|
||||
|
||||
void GlWrapper::showWindow(sp<IAutomotiveDisplayProxyService>& service, uint64_t id) {
|
||||
if (service != nullptr) {
|
||||
service->showWindow(id);
|
||||
} else {
|
||||
LOG(ERROR) << "IAutomotiveDisplayProxyService is not available.";
|
||||
}
|
||||
}
|
||||
|
||||
void GlWrapper::hideWindow(sp<IAutomotiveDisplayProxyService>& service, uint64_t id) {
|
||||
if (service != nullptr) {
|
||||
service->hideWindow(id);
|
||||
} else {
|
||||
LOG(ERROR) << "IAutomotiveDisplayProxyService is not available.";
|
||||
}
|
||||
}
|
||||
|
||||
bool GlWrapper::updateImageTexture(const V1_0::BufferDesc& buffer) {
|
||||
BufferDesc newBuffer = {
|
||||
.buffer =
|
||||
{
|
||||
.nativeHandle = buffer.memHandle,
|
||||
},
|
||||
.pixelSize = buffer.pixelSize,
|
||||
.bufferId = buffer.bufferId,
|
||||
};
|
||||
AHardwareBuffer_Desc* pDesc =
|
||||
reinterpret_cast<AHardwareBuffer_Desc*>(&newBuffer.buffer.description);
|
||||
*pDesc = {
|
||||
.width = buffer.width,
|
||||
.height = buffer.height,
|
||||
.layers = 1,
|
||||
.format = buffer.format,
|
||||
.usage = buffer.usage,
|
||||
};
|
||||
return updateImageTexture(newBuffer);
|
||||
}
|
||||
|
||||
bool GlWrapper::updateImageTexture(const BufferDesc& aFrame) {
|
||||
// If we haven't done it yet, create an "image" object to wrap the gralloc buffer
|
||||
if (mKHRimage == EGL_NO_IMAGE_KHR) {
|
||||
// create a temporary GraphicBuffer to wrap the provided handle
|
||||
const AHardwareBuffer_Desc* pDesc =
|
||||
reinterpret_cast<const AHardwareBuffer_Desc*>(&aFrame.buffer.description);
|
||||
sp<GraphicBuffer> pGfxBuffer = new GraphicBuffer(
|
||||
pDesc->width, pDesc->height, pDesc->format, pDesc->layers, pDesc->usage,
|
||||
pDesc->stride,
|
||||
const_cast<native_handle_t*>(aFrame.buffer.nativeHandle.getNativeHandle()),
|
||||
false /* keep ownership */
|
||||
);
|
||||
if (pGfxBuffer.get() == nullptr) {
|
||||
LOG(ERROR) << "Failed to allocate GraphicBuffer to wrap our native handle";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get a GL compatible reference to the graphics buffer we've been given
|
||||
EGLint eglImageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
|
||||
EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(pGfxBuffer->getNativeBuffer());
|
||||
mKHRimage = eglCreateImageKHR(mDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf,
|
||||
eglImageAttributes);
|
||||
if (mKHRimage == EGL_NO_IMAGE_KHR) {
|
||||
LOG(ERROR) << "Error creating EGLImage: " << getEGLError();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update the texture handle we already created to refer to this gralloc buffer
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, mTextureMap);
|
||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast<GLeglImageOES>(mKHRimage));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GlWrapper::renderImageToScreen() {
|
||||
// Set the viewport
|
||||
glViewport(0, 0, mWidth, mHeight);
|
||||
|
||||
// Clear the color buffer
|
||||
glClearColor(kDefaultColorInRgba[0], kDefaultColorInRgba[1],
|
||||
kDefaultColorInRgba[2], kDefaultColorInRgba[3]);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// Select our screen space simple texture shader
|
||||
glUseProgram(mShaderProgram);
|
||||
|
||||
// Bind the texture and assign it to the shader's sampler
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, mTextureMap);
|
||||
GLint sampler = glGetUniformLocation(mShaderProgram, "tex");
|
||||
glUniform1i(sampler, 0);
|
||||
|
||||
// We want our image to show up opaque regardless of alpha values
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
// Draw a rectangle on the screen
|
||||
GLfloat vertsCarPos[] = {
|
||||
-kDisplayAreaRatio, kDisplayAreaRatio, 0.0f, // left top in window space
|
||||
kDisplayAreaRatio, kDisplayAreaRatio, 0.0f, // right top
|
||||
-kDisplayAreaRatio, -kDisplayAreaRatio, 0.0f, // left bottom
|
||||
kDisplayAreaRatio, -kDisplayAreaRatio, 0.0f // right bottom
|
||||
};
|
||||
|
||||
// NOTE: We didn't flip the image in the texture, so V=0 is actually the top of the image
|
||||
GLfloat vertsCarTex[] = {
|
||||
0.0f, 0.0f, // left top
|
||||
1.0f, 0.0f, // right top
|
||||
0.0f, 1.0f, // left bottom
|
||||
1.0f, 1.0f // right bottom
|
||||
};
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertsCarPos);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, vertsCarTex);
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
// Clean up and flip the rendered result to the front so it is visible
|
||||
glDisableVertexAttribArray(0);
|
||||
glDisableVertexAttribArray(1);
|
||||
|
||||
glFinish();
|
||||
|
||||
eglSwapBuffers(mDisplay, mSurface);
|
||||
}
|
||||
|
||||
} // namespace android::hardware::automotive::evs::V1_1::implementation
|
||||
78
automotive/evs/1.1/default/GlWrapper.h
Normal file
78
automotive/evs/1.1/default/GlWrapper.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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_EVS_V1_1_DISPLAY_GLWRAPPER_H
|
||||
#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_DISPLAY_GLWRAPPER_H
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android/frameworks/automotive/display/1.0/IAutomotiveDisplayProxyService.h>
|
||||
#include <android/hardware/automotive/evs/1.1/types.h>
|
||||
#include <bufferqueueconverter/BufferQueueConverter.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#include <GLES3/gl3.h>
|
||||
#include <GLES3/gl3ext.h>
|
||||
|
||||
namespace android::hardware::automotive::evs::V1_1::implementation {
|
||||
|
||||
using frameworks::automotive::display::V1_0::IAutomotiveDisplayProxyService;
|
||||
using hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer;
|
||||
|
||||
class GlWrapper {
|
||||
public:
|
||||
GlWrapper() : mSurfaceHolder(android::SurfaceHolderUniquePtr(nullptr, nullptr)) {}
|
||||
bool initialize(const sp<IAutomotiveDisplayProxyService>& service, uint64_t displayId);
|
||||
void shutdown();
|
||||
|
||||
bool updateImageTexture(const V1_0::BufferDesc& buffer);
|
||||
bool updateImageTexture(const BufferDesc& buffer);
|
||||
void renderImageToScreen();
|
||||
|
||||
void showWindow(sp<IAutomotiveDisplayProxyService>& service, uint64_t id);
|
||||
void hideWindow(sp<IAutomotiveDisplayProxyService>& service, uint64_t id);
|
||||
|
||||
unsigned getWidth() { return mWidth; };
|
||||
unsigned getHeight() { return mHeight; };
|
||||
|
||||
private:
|
||||
sp<IGraphicBufferProducer> mGfxBufferProducer;
|
||||
|
||||
EGLDisplay mDisplay;
|
||||
EGLSurface mSurface;
|
||||
EGLContext mContext;
|
||||
|
||||
unsigned mWidth = 0;
|
||||
unsigned mHeight = 0;
|
||||
|
||||
EGLImageKHR mKHRimage = EGL_NO_IMAGE_KHR;
|
||||
|
||||
GLuint mTextureMap = 0;
|
||||
GLuint mShaderProgram = 0;
|
||||
|
||||
// Opaque handle for a native hardware buffer defined in
|
||||
// frameworks/native/opengl/include/EGL/eglplatform.h
|
||||
ANativeWindow* mWindow;
|
||||
|
||||
// Pointer to a Surface wrapper.
|
||||
android::SurfaceHolderUniquePtr mSurfaceHolder;
|
||||
};
|
||||
|
||||
} // namespace android::hardware::automotive::evs::V1_1::implementation
|
||||
|
||||
#endif // ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_DISPLAY_GLWRAPPER_H
|
||||
@@ -1,5 +1,8 @@
|
||||
service vendor.evs-hal-mock /vendor/bin/hw/android.hardware.automotive.evs@1.1-service
|
||||
class hal
|
||||
user automotive_evs
|
||||
group automotive_evs
|
||||
disabled
|
||||
priority -20
|
||||
user graphics
|
||||
group automotive_evs camera
|
||||
onrestart restart automotive_display
|
||||
onrestart restart evs_manager
|
||||
disabled # will not automatically start with its class; must be explicitly started.
|
||||
|
||||
@@ -15,12 +15,8 @@
|
||||
-->
|
||||
<manifest version="1.0" type="device" >
|
||||
<hal format="hidl">
|
||||
<name>android.hardware.automotive.evs</name>
|
||||
<transport>hwbinder</transport>
|
||||
<version>1.1</version>
|
||||
<interface>
|
||||
<name>IEvsEnumerator</name>
|
||||
<instance>hw/0</instance>
|
||||
</interface>
|
||||
<name>android.hardware.automotive.evs</name>
|
||||
<fqname>@1.1::IEvsEnumerator/hw/0</fqname>
|
||||
</hal>
|
||||
</manifest>
|
||||
|
||||
@@ -30,31 +30,8 @@
|
||||
|
||||
<!-- camera information -->
|
||||
<camera>
|
||||
<!-- camera group starts -->
|
||||
<group id='group1' synchronized='APPROXIMATE'>
|
||||
<caps>
|
||||
<stream id='0' width='640' height='360' format='RGBA_8888' framerate='30'/>
|
||||
</caps>
|
||||
|
||||
<!-- list of parameters -->
|
||||
<characteristics>
|
||||
<parameter
|
||||
name='REQUEST_AVAILABLE_CAPABILITIES'
|
||||
type='enum'
|
||||
size='1'
|
||||
value='LOGICAL_MULTI_CAMERA'
|
||||
/>
|
||||
<parameter
|
||||
name='LOGICAL_MULTI_CAMERA_PHYSICAL_IDS'
|
||||
type='byte[]'
|
||||
size='1'
|
||||
value='/dev/video1'
|
||||
/>
|
||||
</characteristics>
|
||||
</group>
|
||||
|
||||
<!-- camera device starts -->
|
||||
<device id='/dev/video1' position='rear'>
|
||||
<device id='/dev/video10' position='rear'>
|
||||
<caps>
|
||||
<!-- list of supported controls -->
|
||||
<supported_controls>
|
||||
|
||||
@@ -14,42 +14,42 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "android.hardware.automotive.evs@1.1-service"
|
||||
|
||||
#include <unistd.h>
|
||||
#include "EvsDisplay.h"
|
||||
#include "EvsEnumerator.h"
|
||||
#include "ServiceNames.h"
|
||||
|
||||
#include <hidl/HidlTransportSupport.h>
|
||||
#include <log/log.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
|
||||
#include "ServiceNames.h"
|
||||
#include "EvsEnumerator.h"
|
||||
#include "EvsDisplay.h"
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
// libhidl:
|
||||
using android::frameworks::automotive::display::V1_0::IAutomotiveDisplayProxyService;
|
||||
using android::hardware::configureRpcThreadpool;
|
||||
using android::hardware::joinRpcThreadpool;
|
||||
|
||||
// Generated HIDL files
|
||||
using android::hardware::automotive::evs::V1_0::DisplayState;
|
||||
using android::hardware::automotive::evs::V1_1::IEvsEnumerator;
|
||||
|
||||
// The namespace in which all our implementation code lives
|
||||
using namespace android::hardware::automotive::evs::V1_1::implementation;
|
||||
using namespace android;
|
||||
|
||||
using android::hardware::automotive::evs::V1_1::implementation::EvsEnumerator;
|
||||
|
||||
int main() {
|
||||
ALOGI("EVS Hardware Enumerator service is starting");
|
||||
android::sp<IEvsEnumerator> service = new EvsEnumerator();
|
||||
|
||||
android::sp<IAutomotiveDisplayProxyService> carWindowService =
|
||||
IAutomotiveDisplayProxyService::getService("default");
|
||||
if (carWindowService == nullptr) {
|
||||
ALOGE("Cannot use AutomotiveDisplayProxyService. Exiting.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
android::sp<IEvsEnumerator> service = new EvsEnumerator(carWindowService);
|
||||
|
||||
configureRpcThreadpool(1, true /* callerWillJoin */);
|
||||
|
||||
// Register our service -- if somebody is already registered by our name,
|
||||
// they will be killed (their thread pool will throw an exception).
|
||||
status_t status = service->registerAsService(kEnumeratorServiceName);
|
||||
if (status == OK) {
|
||||
auto status = service->registerAsService(kEnumeratorServiceName);
|
||||
if (status == android::OK) {
|
||||
ALOGD("%s is ready.", kEnumeratorServiceName);
|
||||
joinRpcThreadpool();
|
||||
} else {
|
||||
@@ -58,5 +58,5 @@ int main() {
|
||||
|
||||
// In normal operation, we don't expect the thread pool to exit
|
||||
ALOGE("EVS Hardware Enumerator is shutting down");
|
||||
return 1;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -575,7 +575,8 @@ TEST_P(EvsHidlTest, CameraStreamBuffering) {
|
||||
activeCameras.push_back(pCam);
|
||||
|
||||
// Ask for a very large number of buffers in flight to ensure it errors correctly
|
||||
Return<EvsResult> badResult = pCam->setMaxFramesInFlight(0xFFFFFFFF);
|
||||
Return<EvsResult> badResult =
|
||||
pCam->setMaxFramesInFlight(std::numeric_limits<int32_t>::max());
|
||||
EXPECT_EQ(EvsResult::BUFFER_NOT_AVAILABLE, badResult);
|
||||
|
||||
// Now ask for exactly two buffers in flight as we'll test behavior in that case
|
||||
@@ -660,7 +661,7 @@ TEST_P(EvsHidlTest, CameraToDisplayRoundTrip) {
|
||||
ASSERT_GT(height, 0);
|
||||
|
||||
android::ui::DisplayState* pState = (android::ui::DisplayState*)state.data();
|
||||
ASSERT_NE(pState->layerStack, -1);
|
||||
ASSERT_NE(pState->layerStack, android::ui::INVALID_LAYER_STACK);
|
||||
});
|
||||
|
||||
// Test each reported camera
|
||||
|
||||
61
automotive/evs/aidl/Android.bp
Normal file
61
automotive/evs/aidl/Android.bp
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright (C) 2022 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package {
|
||||
// See: http://go/android-license-faq
|
||||
// A large-scale-change added 'default_applicable_licenses' to import
|
||||
// all of the 'license_kinds' from "hardware_interfaces_license"
|
||||
// to get the below license kinds:
|
||||
// SPDX-license-identifier-Apache-2.0
|
||||
default_applicable_licenses: ["hardware_interfaces_license"],
|
||||
}
|
||||
|
||||
aidl_interface {
|
||||
name: "android.hardware.automotive.evs",
|
||||
vendor_available: true,
|
||||
srcs: [
|
||||
"android/hardware/automotive/evs/*.aidl",
|
||||
],
|
||||
stability: "vintf",
|
||||
imports: [
|
||||
"android.hardware.common-V2",
|
||||
"android.hardware.graphics.common-V3",
|
||||
],
|
||||
backend: {
|
||||
java: {
|
||||
// android.hardware.graphics.common package is not enabled
|
||||
// for Java backend.
|
||||
enabled: false,
|
||||
},
|
||||
cpp: {
|
||||
enabled: false,
|
||||
},
|
||||
ndk: {
|
||||
vndk: {
|
||||
enabled: false,
|
||||
},
|
||||
min_sdk_version: "29",
|
||||
},
|
||||
},
|
||||
versions_with_info: [
|
||||
{
|
||||
version: "1",
|
||||
imports: [
|
||||
"android.hardware.common-V2",
|
||||
"android.hardware.graphics.common-V3",
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
4ebb038edc1da5aa232909382999b2831e70a319
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.automotive.evs;
|
||||
@VintfStability
|
||||
parcelable BufferDesc {
|
||||
android.hardware.graphics.common.HardwareBuffer buffer;
|
||||
int pixelSizeBytes;
|
||||
int bufferId;
|
||||
@utf8InCpp String deviceId;
|
||||
long timestamp;
|
||||
byte[] metadata;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.automotive.evs;
|
||||
@VintfStability
|
||||
parcelable CameraDesc {
|
||||
@utf8InCpp String id;
|
||||
int vendorFlags;
|
||||
byte[] metadata;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.automotive.evs;
|
||||
@Backing(type="int") @VintfStability
|
||||
enum CameraParam {
|
||||
BRIGHTNESS = 0,
|
||||
CONTRAST = 1,
|
||||
AUTOGAIN = 2,
|
||||
GAIN = 3,
|
||||
AUTO_WHITE_BALANCE = 4,
|
||||
WHITE_BALANCE_TEMPERATURE = 5,
|
||||
SHARPNESS = 6,
|
||||
AUTO_EXPOSURE = 7,
|
||||
ABSOLUTE_EXPOSURE = 8,
|
||||
ABSOLUTE_FOCUS = 9,
|
||||
AUTO_FOCUS = 10,
|
||||
ABSOLUTE_ZOOM = 11,
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user