Merge changes from topic "b218588089" into udc-dev
* changes: Implement incomplete or missing IEvsCamera methods Revert "Revert "Remove automotive.evs HIDL entry in compat matrix."" Revert "Revert "Add default implementation of AIDL EVS interface""
This commit is contained in:
commit
82fd5fc3cd
20 changed files with 4409 additions and 200 deletions
|
@ -24,13 +24,56 @@ package {
|
|||
cc_binary {
|
||||
name: "android.hardware.automotive.evs-aidl-default-service",
|
||||
defaults: ["EvsHalDefaults"],
|
||||
local_include_dirs: ["include"],
|
||||
vintf_fragments: ["evs-default-service.xml"],
|
||||
vintf_fragments: ["manifest_evs-default-service.xml"],
|
||||
init_rc: ["evs-default-service.rc"],
|
||||
vendor: true,
|
||||
relative_install_path: "hw",
|
||||
srcs: ["src/*.cpp"],
|
||||
shared_libs: [
|
||||
"libbinder_ndk",
|
||||
cflags: [
|
||||
"-DGL_GLEXT_PROTOTYPES",
|
||||
"-DEGL_EGLEXT_PROTOTYPES",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Werror",
|
||||
"-Wthread-safety",
|
||||
],
|
||||
srcs: [
|
||||
":libgui_frame_event_aidl",
|
||||
"src/*.cpp"
|
||||
],
|
||||
shared_libs: [
|
||||
"android.hardware.graphics.bufferqueue@1.0",
|
||||
"android.hardware.graphics.bufferqueue@2.0",
|
||||
"android.hidl.token@1.0-utils",
|
||||
"libEGL",
|
||||
"libGLESv2",
|
||||
"libbase",
|
||||
"libbinder_ndk",
|
||||
"libbufferqueueconverter",
|
||||
"libcamera_metadata",
|
||||
"libhardware_legacy",
|
||||
"libhidlbase",
|
||||
"liblog",
|
||||
"libnativewindow",
|
||||
"libtinyxml2",
|
||||
"libui",
|
||||
"libutils",
|
||||
"libyuv",
|
||||
],
|
||||
static_libs: [
|
||||
"android.frameworks.automotive.display-V1-ndk",
|
||||
"android.hardware.automotive.evs-V1-ndk",
|
||||
"android.hardware.common-V2-ndk",
|
||||
"libaidlcommonsupport",
|
||||
"libcutils",
|
||||
],
|
||||
local_include_dirs: ["include"],
|
||||
include_dirs: ["frameworks/native/include/"],
|
||||
required: ["evs_mock_hal_configuration.xml"],
|
||||
}
|
||||
|
||||
prebuilt_etc {
|
||||
name: "evs_mock_hal_configuration.xml",
|
||||
soc_specific: true,
|
||||
src: "resources/evs_mock_configuration.xml",
|
||||
sub_dir: "automotive/evs",
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
service vendor.evs-hal-default /vendor/bin/hw/android.hardware.automotive.evs-aidl-default-service
|
||||
class early_hal
|
||||
user automotive_evs
|
||||
group automotive_evs
|
||||
priority -20
|
||||
user graphics
|
||||
group automotive_evs camera
|
||||
onrestart restart cardisplayproxyd
|
||||
onrestart restart evsmanagerd
|
||||
disabled
|
||||
|
|
384
automotive/evs/aidl/impl/default/include/ConfigManager.h
Normal file
384
automotive/evs/aidl/impl/default/include/ConfigManager.h
Normal file
|
@ -0,0 +1,384 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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 "ConfigManagerUtil.h"
|
||||
|
||||
#include <aidl/android/hardware/automotive/evs/CameraParam.h>
|
||||
#include <aidl/android/hardware/graphics/common/PixelFormat.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <system/camera_metadata.h>
|
||||
|
||||
#include <tinyxml2.h>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
/*
|
||||
* Please note that this is different from what is defined in
|
||||
* libhardware/modules/camera/3_4/metadata/types.h; this has one additional
|
||||
* field to store a framerate.
|
||||
*/
|
||||
typedef struct {
|
||||
int id;
|
||||
int width;
|
||||
int height;
|
||||
::aidl::android::hardware::graphics::common::PixelFormat format;
|
||||
int type;
|
||||
int framerate;
|
||||
} StreamConfiguration;
|
||||
|
||||
class ConfigManager final {
|
||||
public:
|
||||
static std::unique_ptr<ConfigManager> Create();
|
||||
ConfigManager(const ConfigManager&) = delete;
|
||||
ConfigManager& operator=(const ConfigManager&) = delete;
|
||||
|
||||
/* Camera device's capabilities and metadata */
|
||||
class CameraInfo {
|
||||
public:
|
||||
CameraInfo() : characteristics(nullptr) {}
|
||||
|
||||
virtual ~CameraInfo();
|
||||
|
||||
/* Allocate memory for camera_metadata_t */
|
||||
bool allocate(size_t entry_cap, size_t data_cap) {
|
||||
if (characteristics != nullptr) {
|
||||
LOG(ERROR) << "Camera metadata is already allocated";
|
||||
return false;
|
||||
}
|
||||
|
||||
characteristics = allocate_camera_metadata(entry_cap, data_cap);
|
||||
return characteristics != nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* List of supported controls that the primary client can program.
|
||||
* Paraemters are stored with its valid range
|
||||
*/
|
||||
std::unordered_map<::aidl::android::hardware::automotive::evs::CameraParam,
|
||||
std::tuple<int32_t, int32_t, int32_t>>
|
||||
controls;
|
||||
|
||||
/*
|
||||
* List of supported output stream configurations.
|
||||
*/
|
||||
std::unordered_map<int32_t, StreamConfiguration> streamConfigurations;
|
||||
|
||||
/*
|
||||
* Internal storage for camera metadata. Each entry holds a pointer to
|
||||
* data and number of elements
|
||||
*/
|
||||
std::unordered_map<camera_metadata_tag_t, std::pair<void*, size_t>> cameraMetadata;
|
||||
|
||||
/* Camera module characteristics */
|
||||
camera_metadata_t* characteristics;
|
||||
};
|
||||
|
||||
class CameraGroupInfo : public CameraInfo {
|
||||
public:
|
||||
CameraGroupInfo() {}
|
||||
|
||||
/* ID of member camera devices */
|
||||
std::unordered_set<std::string> devices;
|
||||
|
||||
/* The capture operation of member camera devices are synchronized */
|
||||
int32_t synchronized = 0;
|
||||
};
|
||||
|
||||
class SystemInfo {
|
||||
public:
|
||||
/* number of available cameras */
|
||||
int32_t numCameras = 0;
|
||||
};
|
||||
|
||||
class DisplayInfo {
|
||||
public:
|
||||
/*
|
||||
* List of supported input stream configurations.
|
||||
*/
|
||||
std::unordered_map<int32_t, StreamConfiguration> streamConfigurations;
|
||||
};
|
||||
|
||||
/*
|
||||
* Return system information
|
||||
*
|
||||
* @return SystemInfo
|
||||
* Constant reference of SystemInfo.
|
||||
*/
|
||||
const SystemInfo& getSystemInfo() {
|
||||
std::unique_lock<std::mutex> lock(mConfigLock);
|
||||
mConfigCond.wait(lock, [this] { return mIsReady; });
|
||||
return mSystemInfo;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a list of camera identifiers
|
||||
*
|
||||
* This function assumes that it is not being called frequently.
|
||||
*
|
||||
* @return std::vector<std::string>
|
||||
* A vector that contains unique camera device identifiers.
|
||||
*/
|
||||
std::vector<std::string> getCameraIdList() {
|
||||
std::unique_lock<std::mutex> lock(mConfigLock);
|
||||
mConfigCond.wait(lock, [this] { return mIsReady; });
|
||||
|
||||
std::vector<std::string> aList;
|
||||
aList.reserve(mCameraInfo.size());
|
||||
for (auto&& v : mCameraInfo) {
|
||||
aList.push_back(v.first);
|
||||
}
|
||||
|
||||
return aList;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a list of camera group identifiers
|
||||
*
|
||||
* This function assumes that it is not being called frequently.
|
||||
*
|
||||
* @return std::vector<std::string>
|
||||
* A vector that contains unique camera device identifiers.
|
||||
*/
|
||||
std::vector<std::string> getCameraGroupIdList() {
|
||||
std::unique_lock<std::mutex> lock(mConfigLock);
|
||||
mConfigCond.wait(lock, [this] { return mIsReady; });
|
||||
|
||||
std::vector<std::string> aList;
|
||||
aList.reserve(mCameraGroups.size());
|
||||
for (auto&& v : mCameraGroups) {
|
||||
aList.push_back(v.first);
|
||||
}
|
||||
|
||||
return aList;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a pointer to the camera group
|
||||
*
|
||||
* @return CameraGroup
|
||||
* A pointer to a camera group identified by a given id.
|
||||
*/
|
||||
std::unique_ptr<CameraGroupInfo>& getCameraGroupInfo(const std::string& gid) {
|
||||
std::unique_lock<std::mutex> lock(mConfigLock);
|
||||
mConfigCond.wait(lock, [this] { return mIsReady; });
|
||||
|
||||
return mCameraGroups[gid];
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a camera metadata
|
||||
*
|
||||
* @param cameraId
|
||||
* Unique camera node identifier in string
|
||||
*
|
||||
* @return 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.
|
||||
*/
|
||||
std::unique_ptr<CameraInfo>& getCameraInfo(const std::string& cameraId) noexcept {
|
||||
std::unique_lock<std::mutex> lock(mConfigLock);
|
||||
mConfigCond.wait(lock, [this] { return mIsReady; });
|
||||
|
||||
return mCameraInfo[cameraId];
|
||||
}
|
||||
|
||||
/*
|
||||
* Tell whether the configuration data is ready to be used
|
||||
*
|
||||
* @return bool
|
||||
* True if configuration data is ready to be consumed.
|
||||
*/
|
||||
bool isReady() const { return mIsReady; }
|
||||
|
||||
private:
|
||||
/* Constructors */
|
||||
ConfigManager() : mBinaryFilePath("") {}
|
||||
|
||||
static std::string_view sConfigDefaultPath;
|
||||
static std::string_view sConfigOverridePath;
|
||||
|
||||
/* System configuration */
|
||||
SystemInfo mSystemInfo;
|
||||
|
||||
/* Internal data structure for camera device information */
|
||||
std::unordered_map<std::string, std::unique_ptr<CameraInfo>> mCameraInfo;
|
||||
|
||||
/* Internal data structure for camera device information */
|
||||
std::unordered_map<std::string, std::unique_ptr<DisplayInfo>> mDisplayInfo;
|
||||
|
||||
/* Camera groups are stored in <groud id, CameraGroup> hash map */
|
||||
std::unordered_map<std::string, std::unique_ptr<CameraGroupInfo>> mCameraGroups;
|
||||
|
||||
/*
|
||||
* Camera positions are stored in <position, camera id set> hash map.
|
||||
* The position must be one of front, rear, left, and right.
|
||||
*/
|
||||
std::unordered_map<std::string, std::unordered_set<std::string>> mCameraPosition;
|
||||
|
||||
/* Configuration data lock */
|
||||
mutable std::mutex mConfigLock;
|
||||
|
||||
/*
|
||||
* This condition is signalled when it completes a configuration data
|
||||
* preparation.
|
||||
*/
|
||||
std::condition_variable mConfigCond;
|
||||
|
||||
/* A path to a binary configuration file */
|
||||
const char* mBinaryFilePath;
|
||||
|
||||
/* Configuration data readiness */
|
||||
bool mIsReady = false;
|
||||
|
||||
/*
|
||||
* Parse a given EVS configuration file and store the information
|
||||
* internally.
|
||||
*
|
||||
* @return bool
|
||||
* True if it completes parsing a file successfully.
|
||||
*/
|
||||
bool readConfigDataFromXML() noexcept;
|
||||
|
||||
/*
|
||||
* read the information of the vehicle
|
||||
*
|
||||
* @param aSysElem
|
||||
* A pointer to "system" XML element.
|
||||
*/
|
||||
void readSystemInfo(const tinyxml2::XMLElement* const aSysElem);
|
||||
|
||||
/*
|
||||
* read the information of camera devices
|
||||
*
|
||||
* @param aCameraElem
|
||||
* A pointer to "camera" XML element that may contain multiple
|
||||
* "device" elements.
|
||||
*/
|
||||
void readCameraInfo(const tinyxml2::XMLElement* const aCameraElem);
|
||||
|
||||
/*
|
||||
* read display device information
|
||||
*
|
||||
* @param aDisplayElem
|
||||
* A pointer to "display" XML element that may contain multiple
|
||||
* "device" elements.
|
||||
*/
|
||||
void readDisplayInfo(const tinyxml2::XMLElement* const aDisplayElem);
|
||||
|
||||
/*
|
||||
* read camera device information
|
||||
*
|
||||
* @param aCamera
|
||||
* A pointer to CameraInfo that will be completed by this
|
||||
* method.
|
||||
* aDeviceElem
|
||||
* A pointer to "device" XML element that contains camera module
|
||||
* capability info and its characteristics.
|
||||
*
|
||||
* @return bool
|
||||
* Return false upon any failure in reading and processing camera
|
||||
* device information.
|
||||
*/
|
||||
bool readCameraDeviceInfo(CameraInfo* aCamera, const tinyxml2::XMLElement* aDeviceElem);
|
||||
|
||||
/*
|
||||
* read camera metadata
|
||||
*
|
||||
* @param aCapElem
|
||||
* A pointer to "cap" XML element.
|
||||
* @param aCamera
|
||||
* A pointer to CameraInfo that is being filled by this method.
|
||||
* @param dataSize
|
||||
* Required size of memory to store camera metadata found in this
|
||||
* method. This is calculated in this method and returned to the
|
||||
* caller for camera_metadata allocation.
|
||||
*
|
||||
* @return size_t
|
||||
* Number of camera metadata entries
|
||||
*/
|
||||
size_t readCameraCapabilities(const tinyxml2::XMLElement* const aCapElem, CameraInfo* aCamera,
|
||||
size_t& dataSize);
|
||||
|
||||
/*
|
||||
* read camera metadata
|
||||
*
|
||||
* @param aParamElem
|
||||
* A pointer to "characteristics" XML element.
|
||||
* @param aCamera
|
||||
* A pointer to CameraInfo that is being filled by this method.
|
||||
* @param dataSize
|
||||
* Required size of memory to store camera metadata found in this
|
||||
* method.
|
||||
*
|
||||
* @return size_t
|
||||
* Number of camera metadata entries
|
||||
*/
|
||||
size_t readCameraMetadata(const tinyxml2::XMLElement* const aParamElem, CameraInfo* aCamera,
|
||||
size_t& dataSize);
|
||||
|
||||
/*
|
||||
* construct camera_metadata_t from camera capabilities and metadata
|
||||
*
|
||||
* @param aCamera
|
||||
* A pointer to CameraInfo that is being filled by this method.
|
||||
* @param totalEntries
|
||||
* Number of camera metadata entries to be added.
|
||||
* @param totalDataSize
|
||||
* Sum of sizes of camera metadata entries to be added.
|
||||
*
|
||||
* @return bool
|
||||
* False if either it fails to allocate memory for camera metadata
|
||||
* or its size is not large enough to add all found camera metadata
|
||||
* entries.
|
||||
*/
|
||||
bool constructCameraMetadata(CameraInfo* aCamera, const size_t totalEntries,
|
||||
const size_t totalDataSize);
|
||||
|
||||
/*
|
||||
* Read configuration data from the binary file
|
||||
*
|
||||
* @return bool
|
||||
* True if it succeeds to read configuration data from a binary
|
||||
* file.
|
||||
*/
|
||||
bool readConfigDataFromBinary();
|
||||
|
||||
/*
|
||||
* Store configuration data to the file
|
||||
*
|
||||
* @return bool
|
||||
* True if it succeeds to serialize mCameraInfo to the file.
|
||||
*/
|
||||
bool writeConfigDataToBinary();
|
||||
|
||||
/*
|
||||
* debugging method to print out all XML elements and their attributes in
|
||||
* logcat message.
|
||||
*
|
||||
* @param aNode
|
||||
* A pointer to the root XML element to navigate.
|
||||
* @param prefix
|
||||
* A prefix to XML string.
|
||||
*/
|
||||
void printElementNames(const tinyxml2::XMLElement* aNode, const std::string& prefix = "") const;
|
||||
};
|
62
automotive/evs/aidl/impl/default/include/ConfigManagerUtil.h
Normal file
62
automotive/evs/aidl/impl/default/include/ConfigManagerUtil.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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 <aidl/android/hardware/automotive/evs/CameraParam.h>
|
||||
#include <aidl/android/hardware/graphics/common/PixelFormat.h>
|
||||
#include <android-base/macros.h>
|
||||
#include <system/camera_metadata.h>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
class ConfigManagerUtil final {
|
||||
public:
|
||||
/**
|
||||
* Convert a given string into V4L2_CID_*
|
||||
*/
|
||||
static bool convertToEvsCameraParam(
|
||||
const std::string& id,
|
||||
::aidl::android::hardware::automotive::evs::CameraParam& camParam);
|
||||
/**
|
||||
* Convert a given string into android.hardware.graphics.common.PixelFormat
|
||||
*/
|
||||
static bool convertToPixelFormat(const std::string& in,
|
||||
::aidl::android::hardware::graphics::common::PixelFormat& out);
|
||||
/**
|
||||
* Convert a given string into corresponding camera metadata data tag defined in
|
||||
* system/media/camera/include/system/camera_metadata_tags.h
|
||||
*/
|
||||
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,
|
||||
const char delimiter = ',');
|
||||
/**
|
||||
* Trim a string
|
||||
*/
|
||||
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);
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(ConfigManagerUtil);
|
||||
};
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
* 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_aidl_impl_evshal_include_DefaultEvsHal_H_
|
||||
#define android_hardware_automotive_evs_aidl_impl_evshal_include_DefaultEvsHal_H_
|
||||
|
||||
#include <aidl/android/hardware/automotive/evs/BnEvsEnumerator.h>
|
||||
|
||||
namespace aidl::android::hardware::automotive::evs::implementation {
|
||||
|
||||
class DefaultEvsEnumerator final
|
||||
: public ::aidl::android::hardware::automotive::evs::BnEvsEnumerator {
|
||||
::ndk::ScopedAStatus isHardware(bool* flag) override;
|
||||
::ndk::ScopedAStatus openCamera(
|
||||
const std::string& cameraId,
|
||||
const ::aidl::android::hardware::automotive::evs::Stream& streamConfig,
|
||||
std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsCamera>* obj) override;
|
||||
::ndk::ScopedAStatus closeCamera(
|
||||
const std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsCamera>& obj)
|
||||
override;
|
||||
::ndk::ScopedAStatus getCameraList(
|
||||
std::vector<::aidl::android::hardware::automotive::evs::CameraDesc>* list) override;
|
||||
::ndk::ScopedAStatus getStreamList(
|
||||
const ::aidl::android::hardware::automotive::evs::CameraDesc& desc,
|
||||
std::vector<::aidl::android::hardware::automotive::evs::Stream>* _aidl_return) override;
|
||||
::ndk::ScopedAStatus openDisplay(
|
||||
int32_t displayId,
|
||||
std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsDisplay>* obj) override;
|
||||
::ndk::ScopedAStatus closeDisplay(
|
||||
const std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsDisplay>& obj)
|
||||
override;
|
||||
::ndk::ScopedAStatus getDisplayIdList(std::vector<uint8_t>* list) override;
|
||||
::ndk::ScopedAStatus getDisplayState(
|
||||
::aidl::android::hardware::automotive::evs::DisplayState* state) override;
|
||||
::ndk::ScopedAStatus registerStatusCallback(
|
||||
const std::shared_ptr<
|
||||
::aidl::android::hardware::automotive::evs::IEvsEnumeratorStatusCallback>&
|
||||
callback) override;
|
||||
::ndk::ScopedAStatus openUltrasonicsArray(
|
||||
const std::string& id,
|
||||
std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsUltrasonicsArray>* obj)
|
||||
override;
|
||||
::ndk::ScopedAStatus closeUltrasonicsArray(
|
||||
const std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsUltrasonicsArray>&
|
||||
arr) override;
|
||||
::ndk::ScopedAStatus getUltrasonicsArrayList(
|
||||
std::vector<::aidl::android::hardware::automotive::evs::UltrasonicsArrayDesc>* list)
|
||||
override;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::automotive::evs::implementation
|
||||
|
||||
#endif // android_hardware_automotive_evs_aidl_impl_evshal_include_DefaultEvsHal_H_
|
134
automotive/evs/aidl/impl/default/include/EvsEnumerator.h
Normal file
134
automotive/evs/aidl/impl/default/include/EvsEnumerator.h
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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 "ConfigManager.h"
|
||||
#include "EvsGlDisplay.h"
|
||||
#include "EvsMockCamera.h"
|
||||
|
||||
#include <aidl/android/frameworks/automotive/display/ICarDisplayProxy.h>
|
||||
#include <aidl/android/hardware/automotive/evs/BnEvsEnumerator.h>
|
||||
#include <aidl/android/hardware/automotive/evs/CameraDesc.h>
|
||||
#include <aidl/android/hardware/automotive/evs/DeviceStatusType.h>
|
||||
#include <aidl/android/hardware/automotive/evs/IEvsCamera.h>
|
||||
#include <aidl/android/hardware/automotive/evs/IEvsEnumeratorStatusCallback.h>
|
||||
#include <aidl/android/hardware/automotive/evs/Stream.h>
|
||||
#include <utils/Thread.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace aidl::android::hardware::automotive::evs::implementation {
|
||||
|
||||
class EvsEnumerator final : public ::aidl::android::hardware::automotive::evs::BnEvsEnumerator {
|
||||
public:
|
||||
// Methods from ::aidl::android::hardware::automotive::evs::IEvsEnumerator
|
||||
ndk::ScopedAStatus isHardware(bool* flag) override;
|
||||
ndk::ScopedAStatus openCamera(const std::string& cameraId, const evs::Stream& streamConfig,
|
||||
std::shared_ptr<evs::IEvsCamera>* obj) override;
|
||||
ndk::ScopedAStatus closeCamera(const std::shared_ptr<evs::IEvsCamera>& obj) override;
|
||||
ndk::ScopedAStatus getCameraList(std::vector<evs::CameraDesc>* _aidl_return) override;
|
||||
ndk::ScopedAStatus getStreamList(const evs::CameraDesc& desc,
|
||||
std::vector<evs::Stream>* _aidl_return) override;
|
||||
ndk::ScopedAStatus openDisplay(int32_t displayId,
|
||||
std::shared_ptr<evs::IEvsDisplay>* obj) override;
|
||||
ndk::ScopedAStatus closeDisplay(const std::shared_ptr<evs::IEvsDisplay>& obj) override;
|
||||
ndk::ScopedAStatus getDisplayIdList(std::vector<uint8_t>* list) override;
|
||||
ndk::ScopedAStatus getDisplayState(evs::DisplayState* state) override;
|
||||
ndk::ScopedAStatus registerStatusCallback(
|
||||
const std::shared_ptr<evs::IEvsEnumeratorStatusCallback>& callback) override;
|
||||
ndk::ScopedAStatus openUltrasonicsArray(
|
||||
const std::string& id, std::shared_ptr<evs::IEvsUltrasonicsArray>* obj) override;
|
||||
ndk::ScopedAStatus closeUltrasonicsArray(
|
||||
const std::shared_ptr<evs::IEvsUltrasonicsArray>& obj) override;
|
||||
ndk::ScopedAStatus getUltrasonicsArrayList(
|
||||
std::vector<evs::UltrasonicsArrayDesc>* list) override;
|
||||
|
||||
// Implementation details
|
||||
EvsEnumerator(const std::shared_ptr<
|
||||
::aidl::android::frameworks::automotive::display::ICarDisplayProxy>&
|
||||
proxyService);
|
||||
|
||||
void notifyDeviceStatusChange(const std::string_view& deviceName, evs::DeviceStatusType type);
|
||||
|
||||
private:
|
||||
struct CameraRecord {
|
||||
evs::CameraDesc desc;
|
||||
std::weak_ptr<EvsMockCamera> activeInstance;
|
||||
|
||||
CameraRecord(const char* cameraId) : desc() { desc.id = cameraId; }
|
||||
};
|
||||
|
||||
class ActiveDisplays {
|
||||
public:
|
||||
struct DisplayInfo {
|
||||
int32_t id{-1};
|
||||
std::weak_ptr<EvsGlDisplay> displayWeak;
|
||||
uintptr_t internalDisplayRawAddr;
|
||||
};
|
||||
|
||||
std::optional<DisplayInfo> popDisplay(int32_t id);
|
||||
|
||||
std::optional<DisplayInfo> popDisplay(const std::shared_ptr<IEvsDisplay>& display);
|
||||
|
||||
std::unordered_map<int32_t, DisplayInfo> getAllDisplays();
|
||||
|
||||
bool tryInsert(int32_t id, const std::shared_ptr<EvsGlDisplay>& display);
|
||||
|
||||
private:
|
||||
std::mutex mMutex;
|
||||
std::unordered_map<int32_t, DisplayInfo> mIdToDisplay GUARDED_BY(mMutex);
|
||||
std::unordered_map<uintptr_t, int32_t> mDisplayToId GUARDED_BY(mMutex);
|
||||
};
|
||||
|
||||
bool checkPermission();
|
||||
void closeCamera_impl(const std::shared_ptr<evs::IEvsCamera>& pCamera,
|
||||
const std::string& cameraId);
|
||||
|
||||
static bool qualifyCaptureDevice(const char* deviceName);
|
||||
static CameraRecord* findCameraById(const std::string& cameraId);
|
||||
static void enumerateCameras();
|
||||
static bool addCaptureDevice(const std::string& deviceName);
|
||||
static bool removeCaptureDevice(const std::string& deviceName);
|
||||
// Enumerate available displays and return an id of the internal display
|
||||
static uint64_t enumerateDisplays();
|
||||
|
||||
static ActiveDisplays& mutableActiveDisplays();
|
||||
|
||||
// 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.
|
||||
// Because our server has a single thread in the thread pool, these values are
|
||||
// never accessed concurrently despite potentially having multiple instance objects
|
||||
// using them.
|
||||
static std::unordered_map<std::string, CameraRecord> sCameraList;
|
||||
// Object destructs if client dies.
|
||||
static std::mutex sLock; // Mutex on shared camera device list.
|
||||
static std::condition_variable sCameraSignal; // Signal on camera device addition.
|
||||
static std::unique_ptr<ConfigManager> sConfigManager; // ConfigManager
|
||||
static std::shared_ptr<::aidl::android::frameworks::automotive::display::ICarDisplayProxy>
|
||||
sDisplayProxy;
|
||||
static std::unordered_map<uint8_t, uint64_t> sDisplayPortList;
|
||||
|
||||
uint64_t mInternalDisplayId;
|
||||
std::shared_ptr<evs::IEvsEnumeratorStatusCallback> mCallback;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::automotive::evs::implementation
|
89
automotive/evs/aidl/impl/default/include/EvsGlDisplay.h
Normal file
89
automotive/evs/aidl/impl/default/include/EvsGlDisplay.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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 "GlWrapper.h"
|
||||
|
||||
#include <aidl/android/frameworks/automotive/display/ICarDisplayProxy.h>
|
||||
#include <aidl/android/hardware/automotive/evs/BnEvsDisplay.h>
|
||||
#include <aidl/android/hardware/automotive/evs/BufferDesc.h>
|
||||
#include <aidl/android/hardware/automotive/evs/DisplayDesc.h>
|
||||
#include <aidl/android/hardware/automotive/evs/DisplayState.h>
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace aidl::android::hardware::automotive::evs::implementation {
|
||||
|
||||
class EvsGlDisplay final : public BnEvsDisplay {
|
||||
public:
|
||||
// Methods from ::aidl::android::hardware::automotive::evs::IEvsDisplay
|
||||
// follow.
|
||||
ndk::ScopedAStatus getDisplayInfo(evs::DisplayDesc* _aidl_return) override;
|
||||
ndk::ScopedAStatus getDisplayState(evs::DisplayState* _aidl_return) override;
|
||||
ndk::ScopedAStatus getTargetBuffer(evs::BufferDesc* _aidl_return) override;
|
||||
ndk::ScopedAStatus returnTargetBufferForDisplay(const evs::BufferDesc& buffer) override;
|
||||
ndk::ScopedAStatus setDisplayState(evs::DisplayState state) override;
|
||||
|
||||
// Implementation details
|
||||
EvsGlDisplay(const std::shared_ptr<automotivedisplay::ICarDisplayProxy>& service,
|
||||
uint64_t displayId);
|
||||
virtual ~EvsGlDisplay() override;
|
||||
|
||||
// This gets called if another caller "steals" ownership of the display
|
||||
void forceShutdown();
|
||||
|
||||
private:
|
||||
// A graphics buffer into which we'll store images. This member variable
|
||||
// will be protected by semaphores.
|
||||
struct BufferRecord {
|
||||
::aidl::android::hardware::graphics::common::HardwareBufferDescription description;
|
||||
buffer_handle_t handle;
|
||||
int fingerprint;
|
||||
} mBuffer;
|
||||
|
||||
// State of a rendering thread
|
||||
enum RenderThreadStates {
|
||||
STOPPED = 0,
|
||||
STOPPING = 1,
|
||||
RUN = 2,
|
||||
};
|
||||
|
||||
uint64_t mDisplayId;
|
||||
evs::DisplayDesc mInfo;
|
||||
evs::DisplayState mRequestedState GUARDED_BY(mLock) = evs::DisplayState::NOT_VISIBLE;
|
||||
std::shared_ptr<automotivedisplay::ICarDisplayProxy> mDisplayProxy;
|
||||
|
||||
GlWrapper mGlWrapper;
|
||||
mutable std::mutex mLock;
|
||||
|
||||
// This tells us whether or not our buffer is in use. Protected by
|
||||
// semaphores.
|
||||
bool mBufferBusy = false;
|
||||
|
||||
// Variables to synchronize a rendering thread w/ main and binder threads
|
||||
std::thread mRenderThread;
|
||||
RenderThreadStates mState GUARDED_BY(mLock) = STOPPED;
|
||||
bool mBufferReady = false;
|
||||
void renderFrames();
|
||||
bool initializeGlContextLocked() REQUIRES(mLock);
|
||||
|
||||
std::condition_variable mBufferReadyToUse;
|
||||
std::condition_variable mBufferReadyToRender;
|
||||
std::condition_variable mBufferDone;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::automotive::evs::implementation
|
166
automotive/evs/aidl/impl/default/include/EvsMockCamera.h
Normal file
166
automotive/evs/aidl/impl/default/include/EvsMockCamera.h
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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 "ConfigManager.h"
|
||||
|
||||
#include <aidl/android/hardware/automotive/evs/BnEvsCamera.h>
|
||||
#include <aidl/android/hardware/automotive/evs/BufferDesc.h>
|
||||
#include <aidl/android/hardware/automotive/evs/CameraDesc.h>
|
||||
#include <aidl/android/hardware/automotive/evs/CameraParam.h>
|
||||
#include <aidl/android/hardware/automotive/evs/EvsResult.h>
|
||||
#include <aidl/android/hardware/automotive/evs/IEvsCameraStream.h>
|
||||
#include <aidl/android/hardware/automotive/evs/IEvsDisplay.h>
|
||||
#include <aidl/android/hardware/automotive/evs/ParameterRange.h>
|
||||
#include <aidl/android/hardware/automotive/evs/Stream.h>
|
||||
// #include <android-base/result.h>
|
||||
#include <android/hardware_buffer.h>
|
||||
#include <ui/GraphicBuffer.h>
|
||||
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
|
||||
namespace aidl::android::hardware::automotive::evs::implementation {
|
||||
|
||||
class EvsMockCamera : public evs::BnEvsCamera {
|
||||
// This prevents constructors from direct access while it allows this class to
|
||||
// be instantiated via ndk::SharedRefBase::make<>.
|
||||
private:
|
||||
struct Sigil {
|
||||
explicit Sigil() = default;
|
||||
};
|
||||
|
||||
public:
|
||||
// Methods from ::android::hardware::automotive::evs::IEvsCamera follow.
|
||||
ndk::ScopedAStatus doneWithFrame(const std::vector<evs::BufferDesc>& buffers) override;
|
||||
ndk::ScopedAStatus forcePrimaryClient(
|
||||
const std::shared_ptr<evs::IEvsDisplay>& display) override;
|
||||
ndk::ScopedAStatus getCameraInfo(evs::CameraDesc* _aidl_return) override;
|
||||
ndk::ScopedAStatus getExtendedInfo(int32_t opaqueIdentifier,
|
||||
std::vector<uint8_t>* value) override;
|
||||
ndk::ScopedAStatus getIntParameter(evs::CameraParam id, std::vector<int32_t>* value) override;
|
||||
ndk::ScopedAStatus getIntParameterRange(evs::CameraParam id,
|
||||
evs::ParameterRange* _aidl_return) override;
|
||||
ndk::ScopedAStatus getParameterList(std::vector<evs::CameraParam>* _aidl_return) override;
|
||||
ndk::ScopedAStatus getPhysicalCameraInfo(const std::string& deviceId,
|
||||
evs::CameraDesc* _aidl_return) override;
|
||||
ndk::ScopedAStatus importExternalBuffers(const std::vector<evs::BufferDesc>& buffers,
|
||||
int32_t* _aidl_return) override;
|
||||
ndk::ScopedAStatus pauseVideoStream() override;
|
||||
ndk::ScopedAStatus resumeVideoStream() override;
|
||||
ndk::ScopedAStatus setExtendedInfo(int32_t opaqueIdentifier,
|
||||
const std::vector<uint8_t>& opaqueValue) override;
|
||||
ndk::ScopedAStatus setIntParameter(evs::CameraParam id, int32_t value,
|
||||
std::vector<int32_t>* effectiveValue) override;
|
||||
ndk::ScopedAStatus setPrimaryClient() override;
|
||||
ndk::ScopedAStatus setMaxFramesInFlight(int32_t bufferCount) override;
|
||||
ndk::ScopedAStatus startVideoStream(
|
||||
const std::shared_ptr<evs::IEvsCameraStream>& receiver) override;
|
||||
ndk::ScopedAStatus stopVideoStream() override;
|
||||
ndk::ScopedAStatus unsetPrimaryClient() override;
|
||||
|
||||
static std::shared_ptr<EvsMockCamera> Create(const char* deviceName);
|
||||
static std::shared_ptr<EvsMockCamera> Create(
|
||||
const char* deviceName, std::unique_ptr<ConfigManager::CameraInfo>& camInfo,
|
||||
const evs::Stream* streamCfg = nullptr);
|
||||
EvsMockCamera(const EvsMockCamera&) = delete;
|
||||
EvsMockCamera& operator=(const EvsMockCamera&) = delete;
|
||||
|
||||
virtual ~EvsMockCamera() override;
|
||||
void shutdown();
|
||||
|
||||
const evs::CameraDesc& getDesc() { return mDescription; }
|
||||
|
||||
// Constructors
|
||||
EvsMockCamera(Sigil sigil, const char* deviceName,
|
||||
std::unique_ptr<ConfigManager::CameraInfo>& camInfo);
|
||||
|
||||
private:
|
||||
// These three functions are expected to be called while mAccessLock is held
|
||||
bool setAvailableFrames_Locked(unsigned bufferCount);
|
||||
unsigned increaseAvailableFrames_Locked(unsigned numToAdd);
|
||||
unsigned decreaseAvailableFrames_Locked(unsigned numToRemove);
|
||||
|
||||
void generateFrames();
|
||||
void fillMockFrame(buffer_handle_t handle, const AHardwareBuffer_Desc* pDesc);
|
||||
void returnBufferLocked(const uint32_t bufferId);
|
||||
ndk::ScopedAStatus stopVideoStream_impl();
|
||||
|
||||
CameraDesc mDescription = {}; // The properties of this camera
|
||||
|
||||
std::thread mCaptureThread; // The thread we'll use to synthesize frames
|
||||
|
||||
// The callback used to deliver each frame
|
||||
std::shared_ptr<evs::IEvsCameraStream> mStream;
|
||||
|
||||
// Horizontal pixel count in the buffers
|
||||
uint32_t mWidth = 0;
|
||||
// Vertical pixel count in the buffers
|
||||
uint32_t mHeight = 0;
|
||||
// Values from android_pixel_format_t
|
||||
uint32_t mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
|
||||
// Values from from Gralloc.h
|
||||
uint64_t mUsage =
|
||||
GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_OFTEN;
|
||||
// Bytes per line in the buffers
|
||||
uint32_t mStride = 0;
|
||||
|
||||
struct BufferRecord {
|
||||
buffer_handle_t handle;
|
||||
bool inUse;
|
||||
|
||||
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
|
||||
|
||||
enum StreamStateValues {
|
||||
STOPPED,
|
||||
RUNNING,
|
||||
STOPPING,
|
||||
DEAD,
|
||||
};
|
||||
StreamStateValues mStreamState;
|
||||
|
||||
// Synchronization necessary to deconflict mCaptureThread from the main service thread
|
||||
std::mutex mAccessLock;
|
||||
|
||||
// Static camera module information
|
||||
std::unique_ptr<ConfigManager::CameraInfo>& mCameraInfo;
|
||||
|
||||
// For the extended info
|
||||
std::unordered_map<uint32_t, std::vector<uint8_t>> mExtInfo;
|
||||
|
||||
// For the camera parameters.
|
||||
struct CameraParameterDesc {
|
||||
CameraParameterDesc(int min = 0, int max = 0, int step = 0, int value = 0) {
|
||||
this->range.min = min;
|
||||
this->range.max = max;
|
||||
this->range.step = step;
|
||||
this->value = value;
|
||||
}
|
||||
|
||||
ParameterRange range;
|
||||
int32_t value;
|
||||
};
|
||||
std::unordered_map<CameraParam, std::shared_ptr<CameraParameterDesc>> mParams;
|
||||
void initializeParameters();
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::automotive::evs::implementation
|
79
automotive/evs/aidl/impl/default/include/GlWrapper.h
Normal file
79
automotive/evs/aidl/impl/default/include/GlWrapper.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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 <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#include <GLES3/gl3.h>
|
||||
#include <GLES3/gl3ext.h>
|
||||
#include <aidl/android/frameworks/automotive/display/ICarDisplayProxy.h>
|
||||
#include <aidl/android/hardware/automotive/evs/BufferDesc.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <bufferqueueconverter/BufferQueueConverter.h>
|
||||
|
||||
namespace aidl::android::hardware::automotive::evs::implementation {
|
||||
|
||||
namespace automotivedisplay = ::aidl::android::frameworks::automotive::display;
|
||||
|
||||
class GlWrapper {
|
||||
public:
|
||||
GlWrapper() : mSurfaceHolder(::android::SurfaceHolderUniquePtr(nullptr, nullptr)) {}
|
||||
bool initialize(const std::shared_ptr<automotivedisplay::ICarDisplayProxy>& svc,
|
||||
uint64_t displayId);
|
||||
void shutdown();
|
||||
|
||||
bool updateImageTexture(
|
||||
buffer_handle_t handle,
|
||||
const ::aidl::android::hardware::graphics::common::HardwareBufferDescription&
|
||||
description);
|
||||
void renderImageToScreen();
|
||||
|
||||
void showWindow(const std::shared_ptr<automotivedisplay::ICarDisplayProxy>& svc,
|
||||
uint64_t displayId);
|
||||
void hideWindow(const std::shared_ptr<automotivedisplay::ICarDisplayProxy>& svc,
|
||||
uint64_t displayId);
|
||||
|
||||
unsigned getWidth() { return mWidth; };
|
||||
unsigned getHeight() { return mHeight; };
|
||||
|
||||
private:
|
||||
::android::sp<::android::hardware::graphics::bufferqueue::V2_0::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 aidl::android::hardware::automotive::evs::implementation
|
|
@ -1,11 +1,7 @@
|
|||
<manifest version="1.0" type="device">
|
||||
<hal format="aidl">
|
||||
<name>android.hardware.automotive.evs</name>
|
||||
<transport>hwbinder</transport>
|
||||
<fqname>IEvsEnumerator/hw/0</fqname>
|
||||
<version>1</version>
|
||||
<interface>
|
||||
<name>IEvsEnumerator</name>
|
||||
<instance>hw/0</instance>
|
||||
</interface>
|
||||
</hal>
|
||||
</manifest>
|
|
@ -0,0 +1,68 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<!-- Copyright (C) 2019 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.
|
||||
-->
|
||||
|
||||
<!-- Exterior View System Example Configuration
|
||||
|
||||
Android Automotive axes are used to define coordinates.
|
||||
See https://source.android.com/devices/sensors/sensor-types#auto_axes
|
||||
|
||||
Use evs_configuration.dtd with xmllint tool, to validate XML configuration file
|
||||
-->
|
||||
|
||||
<configuration>
|
||||
<!-- system configuration -->
|
||||
<system>
|
||||
<!-- number of cameras available to EVS -->
|
||||
<num_cameras value='1'/>
|
||||
</system>
|
||||
|
||||
<!-- camera information -->
|
||||
<camera>
|
||||
<!-- camera device starts -->
|
||||
<device id='/dev/video10' position='rear'>
|
||||
<caps>
|
||||
<!-- list of supported controls -->
|
||||
<supported_controls>
|
||||
<control name='BRIGHTNESS' min='0' max='255'/>
|
||||
<control name='CONTRAST' min='0' max='255'/>
|
||||
</supported_controls>
|
||||
|
||||
<stream id='0' width='640' height='360' format='RGBA_8888' framerate='30'/>
|
||||
</caps>
|
||||
|
||||
<!-- list of parameters -->
|
||||
<characteristics>
|
||||
<!-- Camera intrinsic calibration matrix. See
|
||||
https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html#LENS_INTRINSIC_CALIBRATION
|
||||
-->
|
||||
<parameter
|
||||
name='LENS_INTRINSIC_CALIBRATION'
|
||||
type='float'
|
||||
size='5'
|
||||
value='0.0,0.0,0.0,0.0,0.0'
|
||||
/>
|
||||
</characteristics>
|
||||
</device>
|
||||
</camera>
|
||||
<display>
|
||||
<device id='display0' position='driver'>
|
||||
<caps>
|
||||
<!-- list of supported inpu stream configurations -->
|
||||
<stream id='0' width='1280' height='720' format='RGBA_8888' framerate='30'/>
|
||||
</caps>
|
||||
</device>
|
||||
</display>
|
||||
</configuration>
|
||||
|
992
automotive/evs/aidl/impl/default/src/ConfigManager.cpp
Normal file
992
automotive/evs/aidl/impl/default/src/ConfigManager.cpp
Normal file
|
@ -0,0 +1,992 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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 "ConfigManager.h"
|
||||
|
||||
#include <android-base/parseint.h>
|
||||
#include <hardware/gralloc.h>
|
||||
#include <utils/SystemClock.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
|
||||
namespace {
|
||||
|
||||
using ::aidl::android::hardware::automotive::evs::CameraParam;
|
||||
using ::aidl::android::hardware::graphics::common::PixelFormat;
|
||||
using ::tinyxml2::XMLAttribute;
|
||||
using ::tinyxml2::XMLDocument;
|
||||
using ::tinyxml2::XMLElement;
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string_view ConfigManager::sConfigDefaultPath =
|
||||
"/vendor/etc/automotive/evs/evs_mock_hal_configuration.xml";
|
||||
std::string_view ConfigManager::sConfigOverridePath =
|
||||
"/vendor/etc/automotive/evs/evs_configuration_override.xml";
|
||||
|
||||
void ConfigManager::printElementNames(const XMLElement* rootElem, const std::string& prefix) const {
|
||||
const XMLElement* curElem = rootElem;
|
||||
|
||||
while (curElem != nullptr) {
|
||||
LOG(VERBOSE) << "[ELEM] " << prefix << curElem->Name();
|
||||
const XMLAttribute* curAttr = curElem->FirstAttribute();
|
||||
while (curAttr) {
|
||||
LOG(VERBOSE) << "[ATTR] " << prefix << curAttr->Name() << ": " << curAttr->Value();
|
||||
curAttr = curAttr->Next();
|
||||
}
|
||||
|
||||
/* recursively go down to descendants */
|
||||
printElementNames(curElem->FirstChildElement(), prefix + "\t");
|
||||
|
||||
curElem = curElem->NextSiblingElement();
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigManager::readCameraInfo(const XMLElement* const aCameraElem) {
|
||||
if (aCameraElem == nullptr) {
|
||||
LOG(WARNING) << "XML file does not have required camera element";
|
||||
return;
|
||||
}
|
||||
|
||||
const XMLElement* curElem = aCameraElem->FirstChildElement();
|
||||
while (curElem != nullptr) {
|
||||
if (!strcmp(curElem->Name(), "group")) {
|
||||
/* camera group identifier */
|
||||
const char* id = curElem->FindAttribute("id")->Value();
|
||||
|
||||
/* create a camera group to be filled */
|
||||
CameraGroupInfo* aCamera = new CameraGroupInfo();
|
||||
|
||||
/* read camera device information */
|
||||
if (!readCameraDeviceInfo(aCamera, curElem)) {
|
||||
LOG(WARNING) << "Failed to read a camera information of " << id;
|
||||
delete aCamera;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* camera group synchronization */
|
||||
const char* sync = curElem->FindAttribute("synchronized")->Value();
|
||||
if (!strcmp(sync, "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;
|
||||
} else {
|
||||
aCamera->synchronized = 0; // Not synchronized
|
||||
}
|
||||
|
||||
/* add a group to hash map */
|
||||
mCameraGroups.insert_or_assign(id, std::unique_ptr<CameraGroupInfo>(aCamera));
|
||||
} else if (!std::strcmp(curElem->Name(), "device")) {
|
||||
/* camera unique identifier */
|
||||
const char* id = curElem->FindAttribute("id")->Value();
|
||||
|
||||
/* camera mount location */
|
||||
const char* pos = curElem->FindAttribute("position")->Value();
|
||||
|
||||
/* create a camera device to be filled */
|
||||
CameraInfo* aCamera = new CameraInfo();
|
||||
|
||||
/* read camera device information */
|
||||
if (!readCameraDeviceInfo(aCamera, curElem)) {
|
||||
LOG(WARNING) << "Failed to read a camera information of " << id;
|
||||
delete aCamera;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* store read camera module information */
|
||||
mCameraInfo.insert_or_assign(id, std::unique_ptr<CameraInfo>(aCamera));
|
||||
|
||||
/* assign a camera device to a position group */
|
||||
mCameraPosition[pos].insert(id);
|
||||
} else {
|
||||
/* ignore other device types */
|
||||
LOG(DEBUG) << "Unknown element " << curElem->Name() << " is ignored";
|
||||
}
|
||||
|
||||
curElem = curElem->NextSiblingElement();
|
||||
}
|
||||
}
|
||||
|
||||
bool ConfigManager::readCameraDeviceInfo(CameraInfo* aCamera, const XMLElement* aDeviceElem) {
|
||||
if (aCamera == nullptr || aDeviceElem == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* size information to allocate camera_metadata_t */
|
||||
size_t totalEntries = 0;
|
||||
size_t totalDataSize = 0;
|
||||
|
||||
/* read device capabilities */
|
||||
totalEntries +=
|
||||
readCameraCapabilities(aDeviceElem->FirstChildElement("caps"), aCamera, totalDataSize);
|
||||
|
||||
/* read camera metadata */
|
||||
totalEntries += readCameraMetadata(aDeviceElem->FirstChildElement("characteristics"), aCamera,
|
||||
totalDataSize);
|
||||
|
||||
/* construct camera_metadata_t */
|
||||
if (!constructCameraMetadata(aCamera, totalEntries, totalDataSize)) {
|
||||
LOG(WARNING) << "Either failed to allocate memory or "
|
||||
<< "allocated memory was not large enough";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t ConfigManager::readCameraCapabilities(const XMLElement* const aCapElem, CameraInfo* aCamera,
|
||||
size_t& dataSize) {
|
||||
if (aCapElem == nullptr || aCamera == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string token;
|
||||
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");
|
||||
while (ctrlElem != nullptr) {
|
||||
const char* nameAttr = ctrlElem->FindAttribute("name")->Value();
|
||||
int32_t minVal = INT32_MIN, maxVal = INT32_MAX;
|
||||
if (!android::base::ParseInt(ctrlElem->FindAttribute("min")->Value(), &minVal)) {
|
||||
LOG(WARNING) << "Failed to parse " << ctrlElem->FindAttribute("min")->Value();
|
||||
}
|
||||
|
||||
if (!android::base::ParseInt(ctrlElem->FindAttribute("max")->Value(), &maxVal)) {
|
||||
LOG(WARNING) << "Failed to parse " << ctrlElem->FindAttribute("max")->Value();
|
||||
}
|
||||
|
||||
int32_t stepVal = 1;
|
||||
const XMLAttribute* stepAttr = ctrlElem->FindAttribute("step");
|
||||
if (stepAttr != nullptr) {
|
||||
if (!android::base::ParseInt(stepAttr->Value(), &stepVal)) {
|
||||
LOG(WARNING) << "Failed to parse " << stepAttr->Value();
|
||||
}
|
||||
}
|
||||
|
||||
CameraParam aParam;
|
||||
if (ConfigManagerUtil::convertToEvsCameraParam(nameAttr, aParam)) {
|
||||
aCamera->controls.insert_or_assign(
|
||||
aParam, std::move(std::make_tuple(minVal, maxVal, stepVal)));
|
||||
}
|
||||
|
||||
ctrlElem = ctrlElem->NextSiblingElement("control");
|
||||
}
|
||||
}
|
||||
|
||||
/* a list of camera stream configurations */
|
||||
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");
|
||||
|
||||
int32_t id = -1;
|
||||
int32_t framerate = 0;
|
||||
if (!android::base::ParseInt(idAttr->Value(), &id)) {
|
||||
LOG(WARNING) << "Failed to parse " << idAttr->Value();
|
||||
}
|
||||
if (fpsAttr != nullptr) {
|
||||
if (!android::base::ParseInt(fpsAttr->Value(), &framerate)) {
|
||||
LOG(WARNING) << "Failed to parse " << fpsAttr->Value();
|
||||
}
|
||||
}
|
||||
|
||||
PixelFormat format = PixelFormat::UNSPECIFIED;
|
||||
if (ConfigManagerUtil::convertToPixelFormat(fmtAttr->Value(), format)) {
|
||||
StreamConfiguration cfg = {
|
||||
.id = id,
|
||||
.format = format,
|
||||
.type = ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
|
||||
.framerate = framerate,
|
||||
};
|
||||
|
||||
if (!android::base::ParseInt(widthAttr->Value(), &cfg.width) ||
|
||||
!android::base::ParseInt(heightAttr->Value(), &cfg.height)) {
|
||||
LOG(WARNING) << "Failed to parse " << widthAttr->Value() << " and "
|
||||
<< heightAttr->Value();
|
||||
}
|
||||
aCamera->streamConfigurations.insert_or_assign(id, cfg);
|
||||
}
|
||||
|
||||
curElem = curElem->NextSiblingElement("stream");
|
||||
}
|
||||
|
||||
dataSize = calculate_camera_metadata_entry_data_size(
|
||||
get_camera_metadata_tag_type(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS),
|
||||
aCamera->streamConfigurations.size() * sizeof(StreamConfiguration));
|
||||
|
||||
/* 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) {
|
||||
if (aParamElem == nullptr || aCamera == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
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);
|
||||
|
||||
aCamera->cameraMetadata.insert_or_assign(tag, std::make_pair(data, count));
|
||||
|
||||
++numEntries;
|
||||
dataSize += calculate_camera_metadata_entry_data_size(
|
||||
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];
|
||||
if (ConfigManagerUtil::convertToCameraCapability(
|
||||
curElem->FindAttribute("value")->Value(), *data)) {
|
||||
aCamera->cameraMetadata.insert_or_assign(tag,
|
||||
std::make_pair((void*)data, 1));
|
||||
|
||||
++numEntries;
|
||||
dataSize += calculate_camera_metadata_entry_data_size(
|
||||
get_camera_metadata_tag_type(tag), 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
/* replace commas with null char */
|
||||
char* p = data;
|
||||
while (*p != '\0') {
|
||||
if (*p == ',') {
|
||||
*p = '\0';
|
||||
}
|
||||
++p;
|
||||
}
|
||||
|
||||
aCamera->cameraMetadata.insert_or_assign(tag,
|
||||
std::make_pair((void*)data, len + 1));
|
||||
|
||||
++numEntries;
|
||||
dataSize += calculate_camera_metadata_entry_data_size(
|
||||
get_camera_metadata_tag_type(tag), len);
|
||||
break;
|
||||
}
|
||||
|
||||
/* TODO(b/140416878): add vendor-defined/custom tag support */
|
||||
default:
|
||||
LOG(WARNING) << "Parameter " << curElem->FindAttribute("name")->Value()
|
||||
<< " is not supported";
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LOG(WARNING) << "Unsupported metadata tag " << curElem->FindAttribute("name")->Value()
|
||||
<< " is found.";
|
||||
}
|
||||
|
||||
curElem = curElem->NextSiblingElement("parameter");
|
||||
}
|
||||
|
||||
return numEntries;
|
||||
}
|
||||
|
||||
bool ConfigManager::constructCameraMetadata(CameraInfo* aCamera, size_t totalEntries,
|
||||
size_t totalDataSize) {
|
||||
if (aCamera == nullptr || !aCamera->allocate(totalEntries, totalDataSize)) {
|
||||
LOG(ERROR) << "Failed to allocate memory for camera metadata";
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t numStreamConfigs = aCamera->streamConfigurations.size();
|
||||
std::unique_ptr<int32_t[]> data(new int32_t[sizeof(StreamConfiguration) * numStreamConfigs]);
|
||||
int32_t* ptr = data.get();
|
||||
for (auto& cfg : aCamera->streamConfigurations) {
|
||||
memcpy(ptr, &cfg.second, sizeof(StreamConfiguration));
|
||||
ptr += sizeof(StreamConfiguration);
|
||||
}
|
||||
int32_t err = add_camera_metadata_entry(
|
||||
aCamera->characteristics, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, data.get(),
|
||||
numStreamConfigs * sizeof(StreamConfiguration));
|
||||
|
||||
if (err) {
|
||||
LOG(ERROR) << "Failed to add stream configurations to metadata, ignored";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
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, entry.second);
|
||||
if (err) {
|
||||
LOG(ERROR) << "Failed to add an entry with a tag, " << std::hex << tag;
|
||||
|
||||
/* may exceed preallocated capacity */
|
||||
LOG(ERROR) << "Camera metadata has "
|
||||
<< get_camera_metadata_entry_count(aCamera->characteristics) << " / "
|
||||
<< get_camera_metadata_entry_capacity(aCamera->characteristics)
|
||||
<< " entries and "
|
||||
<< get_camera_metadata_data_count(aCamera->characteristics) << " / "
|
||||
<< get_camera_metadata_data_capacity(aCamera->characteristics)
|
||||
<< " bytes are filled.";
|
||||
LOG(ERROR) << "\tCurrent metadata entry requires "
|
||||
<< calculate_camera_metadata_entry_data_size(tag, entry.second) << " bytes.";
|
||||
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(VERBOSE) << "Camera metadata has "
|
||||
<< get_camera_metadata_entry_count(aCamera->characteristics) << " / "
|
||||
<< get_camera_metadata_entry_capacity(aCamera->characteristics) << " entries and "
|
||||
<< get_camera_metadata_data_count(aCamera->characteristics) << " / "
|
||||
<< get_camera_metadata_data_capacity(aCamera->characteristics)
|
||||
<< " bytes are filled.";
|
||||
return success;
|
||||
}
|
||||
|
||||
void ConfigManager::readSystemInfo(const XMLElement* const aSysElem) {
|
||||
if (aSysElem == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Please note that this function assumes that a given system XML element
|
||||
* and its child elements follow DTD. If it does not, it will cause a
|
||||
* segmentation fault due to the failure of finding expected attributes.
|
||||
*/
|
||||
|
||||
/* read number of cameras available in the system */
|
||||
const XMLElement* xmlElem = aSysElem->FirstChildElement("num_cameras");
|
||||
if (xmlElem != nullptr) {
|
||||
if (!android::base::ParseInt(xmlElem->FindAttribute("value")->Value(),
|
||||
&mSystemInfo.numCameras)) {
|
||||
LOG(WARNING) << "Failed to parse " << xmlElem->FindAttribute("value")->Value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigManager::readDisplayInfo(const XMLElement* const aDisplayElem) {
|
||||
if (aDisplayElem == nullptr) {
|
||||
LOG(WARNING) << "XML file does not have required camera element";
|
||||
return;
|
||||
}
|
||||
|
||||
const XMLElement* curDev = aDisplayElem->FirstChildElement("device");
|
||||
while (curDev != nullptr) {
|
||||
const char* id = curDev->FindAttribute("id")->Value();
|
||||
std::unique_ptr<DisplayInfo> dpy(new DisplayInfo());
|
||||
if (dpy == nullptr) {
|
||||
LOG(ERROR) << "Failed to allocate memory for DisplayInfo";
|
||||
return;
|
||||
}
|
||||
|
||||
const XMLElement* cap = curDev->FirstChildElement("caps");
|
||||
if (cap != nullptr) {
|
||||
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");
|
||||
|
||||
int32_t id = -1;
|
||||
if (!android::base::ParseInt(idAttr->Value(), &id)) {
|
||||
LOG(WARNING) << "Failed to parse " << idAttr->Value();
|
||||
}
|
||||
PixelFormat format = PixelFormat::UNSPECIFIED;
|
||||
if (ConfigManagerUtil::convertToPixelFormat(fmtAttr->Value(), format)) {
|
||||
StreamConfiguration cfg = {
|
||||
.id = id,
|
||||
.format = format,
|
||||
.type = ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT,
|
||||
};
|
||||
if (!android::base::ParseInt(widthAttr->Value(), &cfg.width) ||
|
||||
!android::base::ParseInt(heightAttr->Value(), &cfg.height)) {
|
||||
LOG(WARNING) << "Failed to parse " << widthAttr->Value() << " and "
|
||||
<< heightAttr->Value();
|
||||
}
|
||||
dpy->streamConfigurations.insert_or_assign(id, cfg);
|
||||
}
|
||||
|
||||
curStream = curStream->NextSiblingElement("stream");
|
||||
}
|
||||
}
|
||||
|
||||
mDisplayInfo.insert_or_assign(id, std::move(dpy));
|
||||
curDev = curDev->NextSiblingElement("device");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool ConfigManager::readConfigDataFromXML() noexcept {
|
||||
XMLDocument xmlDoc;
|
||||
|
||||
const int64_t parsingStart = android::elapsedRealtimeNano();
|
||||
|
||||
/* load and parse a configuration file */
|
||||
xmlDoc.LoadFile(sConfigOverridePath.data());
|
||||
if (xmlDoc.ErrorID() != tinyxml2::XML_SUCCESS) {
|
||||
xmlDoc.LoadFile(sConfigDefaultPath.data());
|
||||
if (xmlDoc.ErrorID() != tinyxml2::XML_SUCCESS) {
|
||||
LOG(ERROR) << "Failed to load and/or parse a configuration file, " << xmlDoc.ErrorStr();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* retrieve the root element */
|
||||
const XMLElement* rootElem = xmlDoc.RootElement();
|
||||
if (std::strcmp(rootElem->Name(), "configuration") != 0) {
|
||||
LOG(ERROR) << "A configuration file is not in the required format. "
|
||||
<< "See /etc/automotive/evs/evs_configuration.dtd";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(mConfigLock);
|
||||
|
||||
/*
|
||||
* parse camera information; this needs to be done before reading system
|
||||
* information
|
||||
*/
|
||||
readCameraInfo(rootElem->FirstChildElement("camera"));
|
||||
|
||||
/* parse system information */
|
||||
readSystemInfo(rootElem->FirstChildElement("system"));
|
||||
|
||||
/* parse display information */
|
||||
readDisplayInfo(rootElem->FirstChildElement("display"));
|
||||
|
||||
/* configuration data is ready to be consumed */
|
||||
mIsReady = true;
|
||||
|
||||
/* notify that configuration data is ready */
|
||||
lock.unlock();
|
||||
mConfigCond.notify_all();
|
||||
|
||||
const int64_t parsingEnd = android::elapsedRealtimeNano();
|
||||
LOG(INFO) << "Parsing configuration file takes " << std::scientific
|
||||
<< (double)(parsingEnd - parsingStart) / 1000000.0 << " ms.";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConfigManager::readConfigDataFromBinary() {
|
||||
/* Temporary buffer to hold configuration data read from a binary file */
|
||||
char mBuffer[1024];
|
||||
|
||||
std::fstream srcFile;
|
||||
const int64_t readStart = android::elapsedRealtimeNano();
|
||||
|
||||
srcFile.open(mBinaryFilePath, std::fstream::in | std::fstream::binary);
|
||||
if (!srcFile) {
|
||||
LOG(ERROR) << "Failed to open a source binary file, " << mBinaryFilePath;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(mConfigLock);
|
||||
mIsReady = false;
|
||||
|
||||
/* read configuration data into the internal buffer */
|
||||
srcFile.read(mBuffer, sizeof(mBuffer));
|
||||
LOG(VERBOSE) << __FUNCTION__ << ": " << srcFile.gcount() << " bytes are read.";
|
||||
char* p = mBuffer;
|
||||
size_t sz = 0;
|
||||
|
||||
/* read number of camera group information entries */
|
||||
const size_t ngrps = *(reinterpret_cast<size_t*>(p));
|
||||
p += sizeof(size_t);
|
||||
|
||||
/* read each camera information entry */
|
||||
for (size_t cidx = 0; cidx < ngrps; ++cidx) {
|
||||
/* read camera identifier */
|
||||
std::string cameraId = *(reinterpret_cast<std::string*>(p));
|
||||
p += sizeof(std::string);
|
||||
|
||||
/* size of camera_metadata_t */
|
||||
const size_t num_entry = *(reinterpret_cast<size_t*>(p));
|
||||
p += sizeof(size_t);
|
||||
const size_t num_data = *(reinterpret_cast<size_t*>(p));
|
||||
p += sizeof(size_t);
|
||||
|
||||
/* create CameraInfo and add it to hash map */
|
||||
std::unique_ptr<ConfigManager::CameraGroupInfo> aCamera;
|
||||
if (aCamera == nullptr || !aCamera->allocate(num_entry, num_data)) {
|
||||
LOG(ERROR) << "Failed to create new CameraInfo object";
|
||||
mCameraInfo.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* controls */
|
||||
typedef struct {
|
||||
CameraParam cid;
|
||||
int32_t min;
|
||||
int32_t max;
|
||||
int32_t step;
|
||||
} CameraCtrl;
|
||||
sz = *(reinterpret_cast<size_t*>(p));
|
||||
p += sizeof(size_t);
|
||||
CameraCtrl* ptr = reinterpret_cast<CameraCtrl*>(p);
|
||||
for (size_t idx = 0; idx < sz; ++idx) {
|
||||
CameraCtrl temp = *ptr++;
|
||||
aCamera->controls.insert_or_assign(
|
||||
temp.cid, std::move(std::make_tuple(temp.min, temp.max, temp.step)));
|
||||
}
|
||||
p = reinterpret_cast<char*>(ptr);
|
||||
|
||||
/* stream configurations */
|
||||
sz = *(reinterpret_cast<size_t*>(p));
|
||||
p += sizeof(size_t);
|
||||
int32_t* i32_ptr = reinterpret_cast<int32_t*>(p);
|
||||
for (size_t idx = 0; idx < sz; ++idx) {
|
||||
const int32_t id = *i32_ptr++;
|
||||
|
||||
StreamConfiguration temp;
|
||||
memcpy(&temp, i32_ptr, sizeof(StreamConfiguration));
|
||||
i32_ptr += sizeof(StreamConfiguration);
|
||||
aCamera->streamConfigurations.insert_or_assign(id, temp);
|
||||
}
|
||||
p = reinterpret_cast<char*>(i32_ptr);
|
||||
|
||||
/* synchronization */
|
||||
aCamera->synchronized = *(reinterpret_cast<int32_t*>(p));
|
||||
p += sizeof(int32_t);
|
||||
|
||||
for (size_t idx = 0; idx < num_entry; ++idx) {
|
||||
/* Read camera metadata entries */
|
||||
camera_metadata_tag_t tag = *reinterpret_cast<camera_metadata_tag_t*>(p);
|
||||
p += sizeof(camera_metadata_tag_t);
|
||||
const size_t count = *reinterpret_cast<size_t*>(p);
|
||||
p += sizeof(size_t);
|
||||
|
||||
const int32_t type = get_camera_metadata_tag_type(tag);
|
||||
switch (type) {
|
||||
case TYPE_BYTE: {
|
||||
add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
|
||||
p += count * sizeof(uint8_t);
|
||||
break;
|
||||
}
|
||||
case TYPE_INT32: {
|
||||
add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
|
||||
p += count * sizeof(int32_t);
|
||||
break;
|
||||
}
|
||||
case TYPE_FLOAT: {
|
||||
add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
|
||||
p += count * sizeof(float);
|
||||
break;
|
||||
}
|
||||
case TYPE_INT64: {
|
||||
add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
|
||||
p += count * sizeof(int64_t);
|
||||
break;
|
||||
}
|
||||
case TYPE_DOUBLE: {
|
||||
add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
|
||||
p += count * sizeof(double);
|
||||
break;
|
||||
}
|
||||
case TYPE_RATIONAL:
|
||||
p += count * sizeof(camera_metadata_rational_t);
|
||||
break;
|
||||
default:
|
||||
LOG(WARNING) << "Type " << type << " is unknown; "
|
||||
<< "data may be corrupted.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mCameraInfo.insert_or_assign(cameraId, std::move(aCamera));
|
||||
}
|
||||
|
||||
/* read number of camera information entries */
|
||||
const size_t ncams = *(reinterpret_cast<size_t*>(p));
|
||||
p += sizeof(size_t);
|
||||
|
||||
/* read each camera information entry */
|
||||
for (size_t cidx = 0; cidx < ncams; ++cidx) {
|
||||
/* read camera identifier */
|
||||
std::string cameraId = *(reinterpret_cast<std::string*>(p));
|
||||
p += sizeof(std::string);
|
||||
|
||||
/* size of camera_metadata_t */
|
||||
const size_t num_entry = *(reinterpret_cast<size_t*>(p));
|
||||
p += sizeof(size_t);
|
||||
const size_t num_data = *(reinterpret_cast<size_t*>(p));
|
||||
p += sizeof(size_t);
|
||||
|
||||
/* create CameraInfo and add it to hash map */
|
||||
std::unique_ptr<ConfigManager::CameraInfo> aCamera;
|
||||
if (aCamera == nullptr || !aCamera->allocate(num_entry, num_data)) {
|
||||
LOG(ERROR) << "Failed to create new CameraInfo object";
|
||||
mCameraInfo.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* controls */
|
||||
typedef struct {
|
||||
CameraParam cid;
|
||||
int32_t min;
|
||||
int32_t max;
|
||||
int32_t step;
|
||||
} CameraCtrl;
|
||||
sz = *(reinterpret_cast<size_t*>(p));
|
||||
p += sizeof(size_t);
|
||||
CameraCtrl* ptr = reinterpret_cast<CameraCtrl*>(p);
|
||||
for (size_t idx = 0; idx < sz; ++idx) {
|
||||
CameraCtrl temp = *ptr++;
|
||||
aCamera->controls.insert_or_assign(
|
||||
temp.cid, std::move(std::make_tuple(temp.min, temp.max, temp.step)));
|
||||
}
|
||||
p = reinterpret_cast<char*>(ptr);
|
||||
|
||||
/* stream configurations */
|
||||
sz = *(reinterpret_cast<size_t*>(p));
|
||||
p += sizeof(size_t);
|
||||
int32_t* i32_ptr = reinterpret_cast<int32_t*>(p);
|
||||
for (size_t idx = 0; idx < sz; ++idx) {
|
||||
const int32_t id = *i32_ptr++;
|
||||
|
||||
StreamConfiguration temp;
|
||||
memcpy(&temp, i32_ptr, sizeof(StreamConfiguration));
|
||||
i32_ptr += sizeof(StreamConfiguration);
|
||||
aCamera->streamConfigurations.insert_or_assign(id, temp);
|
||||
}
|
||||
p = reinterpret_cast<char*>(i32_ptr);
|
||||
|
||||
for (size_t idx = 0; idx < num_entry; ++idx) {
|
||||
/* Read camera metadata entries */
|
||||
camera_metadata_tag_t tag = *reinterpret_cast<camera_metadata_tag_t*>(p);
|
||||
p += sizeof(camera_metadata_tag_t);
|
||||
const size_t count = *reinterpret_cast<size_t*>(p);
|
||||
p += sizeof(size_t);
|
||||
|
||||
const int32_t type = get_camera_metadata_tag_type(tag);
|
||||
switch (type) {
|
||||
case TYPE_BYTE: {
|
||||
add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
|
||||
p += count * sizeof(uint8_t);
|
||||
break;
|
||||
}
|
||||
case TYPE_INT32: {
|
||||
add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
|
||||
p += count * sizeof(int32_t);
|
||||
break;
|
||||
}
|
||||
case TYPE_FLOAT: {
|
||||
add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
|
||||
p += count * sizeof(float);
|
||||
break;
|
||||
}
|
||||
case TYPE_INT64: {
|
||||
add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
|
||||
p += count * sizeof(int64_t);
|
||||
break;
|
||||
}
|
||||
case TYPE_DOUBLE: {
|
||||
add_camera_metadata_entry(aCamera->characteristics, tag, p, count);
|
||||
p += count * sizeof(double);
|
||||
break;
|
||||
}
|
||||
case TYPE_RATIONAL:
|
||||
p += count * sizeof(camera_metadata_rational_t);
|
||||
break;
|
||||
default:
|
||||
LOG(WARNING) << "Type " << type << " is unknown; "
|
||||
<< "data may be corrupted.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mCameraInfo.insert_or_assign(cameraId, std::move(aCamera));
|
||||
}
|
||||
|
||||
mIsReady = true;
|
||||
|
||||
/* notify that configuration data is ready */
|
||||
lock.unlock();
|
||||
mConfigCond.notify_all();
|
||||
|
||||
int64_t readEnd = android::elapsedRealtimeNano();
|
||||
LOG(INFO) << __FUNCTION__ << " takes " << std::scientific
|
||||
<< (double)(readEnd - readStart) / 1000000.0 << " ms.";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConfigManager::writeConfigDataToBinary() {
|
||||
std::fstream outFile;
|
||||
|
||||
const int64_t writeStart = android::elapsedRealtimeNano();
|
||||
|
||||
outFile.open(mBinaryFilePath, std::fstream::out | std::fstream::binary);
|
||||
if (!outFile) {
|
||||
LOG(ERROR) << "Failed to open a destination binary file, " << mBinaryFilePath;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* lock a configuration data while it's being written to the filesystem */
|
||||
std::lock_guard<std::mutex> lock(mConfigLock);
|
||||
|
||||
/* write camera group information */
|
||||
size_t sz = mCameraGroups.size();
|
||||
outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
|
||||
for (auto&& [camId, camInfo] : mCameraGroups) {
|
||||
LOG(INFO) << "Storing camera group " << camId;
|
||||
|
||||
/* write a camera identifier string */
|
||||
outFile.write(reinterpret_cast<const char*>(&camId), sizeof(std::string));
|
||||
|
||||
/* controls */
|
||||
sz = camInfo->controls.size();
|
||||
outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
|
||||
for (auto&& [ctrl, range] : camInfo->controls) {
|
||||
outFile.write(reinterpret_cast<const char*>(&ctrl), sizeof(CameraParam));
|
||||
outFile.write(reinterpret_cast<const char*>(&std::get<0>(range)), sizeof(int32_t));
|
||||
outFile.write(reinterpret_cast<const char*>(&std::get<1>(range)), sizeof(int32_t));
|
||||
outFile.write(reinterpret_cast<const char*>(&std::get<2>(range)), sizeof(int32_t));
|
||||
}
|
||||
|
||||
/* stream configurations */
|
||||
sz = camInfo->streamConfigurations.size();
|
||||
outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
|
||||
for (auto&& [sid, cfg] : camInfo->streamConfigurations) {
|
||||
outFile.write(reinterpret_cast<const char*>(sid), sizeof(int32_t));
|
||||
outFile.write(reinterpret_cast<const char*>(&cfg), sizeof(cfg));
|
||||
}
|
||||
|
||||
/* synchronization */
|
||||
outFile.write(reinterpret_cast<const char*>(&camInfo->synchronized), sizeof(int32_t));
|
||||
|
||||
/* size of camera_metadata_t */
|
||||
size_t num_entry = 0;
|
||||
size_t num_data = 0;
|
||||
if (camInfo->characteristics != nullptr) {
|
||||
num_entry = get_camera_metadata_entry_count(camInfo->characteristics);
|
||||
num_data = get_camera_metadata_data_count(camInfo->characteristics);
|
||||
}
|
||||
outFile.write(reinterpret_cast<const char*>(&num_entry), sizeof(size_t));
|
||||
outFile.write(reinterpret_cast<const char*>(&num_data), sizeof(size_t));
|
||||
|
||||
/* write each camera metadata entry */
|
||||
if (num_entry > 0) {
|
||||
camera_metadata_entry_t entry;
|
||||
for (size_t idx = 0; idx < num_entry; ++idx) {
|
||||
if (get_camera_metadata_entry(camInfo->characteristics, idx, &entry)) {
|
||||
LOG(ERROR) << "Failed to retrieve camera metadata entry " << idx;
|
||||
outFile.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
outFile.write(reinterpret_cast<const char*>(&entry.tag), sizeof(entry.tag));
|
||||
outFile.write(reinterpret_cast<const char*>(&entry.count), sizeof(entry.count));
|
||||
|
||||
int32_t type = get_camera_metadata_tag_type(entry.tag);
|
||||
switch (type) {
|
||||
case TYPE_BYTE:
|
||||
outFile.write(reinterpret_cast<const char*>(entry.data.u8),
|
||||
sizeof(uint8_t) * entry.count);
|
||||
break;
|
||||
case TYPE_INT32:
|
||||
outFile.write(reinterpret_cast<const char*>(entry.data.i32),
|
||||
sizeof(int32_t) * entry.count);
|
||||
break;
|
||||
case TYPE_FLOAT:
|
||||
outFile.write(reinterpret_cast<const char*>(entry.data.f),
|
||||
sizeof(float) * entry.count);
|
||||
break;
|
||||
case TYPE_INT64:
|
||||
outFile.write(reinterpret_cast<const char*>(entry.data.i64),
|
||||
sizeof(int64_t) * entry.count);
|
||||
break;
|
||||
case TYPE_DOUBLE:
|
||||
outFile.write(reinterpret_cast<const char*>(entry.data.d),
|
||||
sizeof(double) * entry.count);
|
||||
break;
|
||||
case TYPE_RATIONAL:
|
||||
[[fallthrough]];
|
||||
default:
|
||||
LOG(WARNING) << "Type " << type << " is not supported.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* write camera device information */
|
||||
sz = mCameraInfo.size();
|
||||
outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
|
||||
for (auto&& [camId, camInfo] : mCameraInfo) {
|
||||
LOG(INFO) << "Storing camera " << camId;
|
||||
|
||||
/* write a camera identifier string */
|
||||
outFile.write(reinterpret_cast<const char*>(&camId), sizeof(std::string));
|
||||
|
||||
/* controls */
|
||||
sz = camInfo->controls.size();
|
||||
outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
|
||||
for (auto& [ctrl, range] : camInfo->controls) {
|
||||
outFile.write(reinterpret_cast<const char*>(&ctrl), sizeof(CameraParam));
|
||||
outFile.write(reinterpret_cast<const char*>(&std::get<0>(range)), sizeof(int32_t));
|
||||
outFile.write(reinterpret_cast<const char*>(&std::get<1>(range)), sizeof(int32_t));
|
||||
outFile.write(reinterpret_cast<const char*>(&std::get<2>(range)), sizeof(int32_t));
|
||||
}
|
||||
|
||||
/* stream configurations */
|
||||
sz = camInfo->streamConfigurations.size();
|
||||
outFile.write(reinterpret_cast<const char*>(&sz), sizeof(size_t));
|
||||
for (auto&& [sid, cfg] : camInfo->streamConfigurations) {
|
||||
outFile.write(reinterpret_cast<const char*>(sid), sizeof(int32_t));
|
||||
outFile.write(reinterpret_cast<const char*>(&cfg), sizeof(cfg));
|
||||
}
|
||||
|
||||
/* size of camera_metadata_t */
|
||||
size_t num_entry = 0;
|
||||
size_t num_data = 0;
|
||||
if (camInfo->characteristics != nullptr) {
|
||||
num_entry = get_camera_metadata_entry_count(camInfo->characteristics);
|
||||
num_data = get_camera_metadata_data_count(camInfo->characteristics);
|
||||
}
|
||||
outFile.write(reinterpret_cast<const char*>(&num_entry), sizeof(size_t));
|
||||
outFile.write(reinterpret_cast<const char*>(&num_data), sizeof(size_t));
|
||||
|
||||
/* write each camera metadata entry */
|
||||
if (num_entry > 0) {
|
||||
camera_metadata_entry_t entry;
|
||||
for (size_t idx = 0; idx < num_entry; ++idx) {
|
||||
if (get_camera_metadata_entry(camInfo->characteristics, idx, &entry)) {
|
||||
LOG(ERROR) << "Failed to retrieve camera metadata entry " << idx;
|
||||
outFile.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
outFile.write(reinterpret_cast<const char*>(&entry.tag), sizeof(entry.tag));
|
||||
outFile.write(reinterpret_cast<const char*>(&entry.count), sizeof(entry.count));
|
||||
|
||||
int32_t type = get_camera_metadata_tag_type(entry.tag);
|
||||
switch (type) {
|
||||
case TYPE_BYTE:
|
||||
outFile.write(reinterpret_cast<const char*>(entry.data.u8),
|
||||
sizeof(uint8_t) * entry.count);
|
||||
break;
|
||||
case TYPE_INT32:
|
||||
outFile.write(reinterpret_cast<const char*>(entry.data.i32),
|
||||
sizeof(int32_t) * entry.count);
|
||||
break;
|
||||
case TYPE_FLOAT:
|
||||
outFile.write(reinterpret_cast<const char*>(entry.data.f),
|
||||
sizeof(float) * entry.count);
|
||||
break;
|
||||
case TYPE_INT64:
|
||||
outFile.write(reinterpret_cast<const char*>(entry.data.i64),
|
||||
sizeof(int64_t) * entry.count);
|
||||
break;
|
||||
case TYPE_DOUBLE:
|
||||
outFile.write(reinterpret_cast<const char*>(entry.data.d),
|
||||
sizeof(double) * entry.count);
|
||||
break;
|
||||
case TYPE_RATIONAL:
|
||||
[[fallthrough]];
|
||||
default:
|
||||
LOG(WARNING) << "Type " << type << " is not supported.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outFile.close();
|
||||
int64_t writeEnd = android::elapsedRealtimeNano();
|
||||
LOG(INFO) << __FUNCTION__ << " takes " << std::scientific
|
||||
<< (double)(writeEnd - writeStart) / 1000000.0 << " ms.";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<ConfigManager> ConfigManager::Create() {
|
||||
std::unique_ptr<ConfigManager> cfgMgr(new ConfigManager());
|
||||
|
||||
/*
|
||||
* Read a configuration from XML file
|
||||
*
|
||||
* If this is too slow, ConfigManager::readConfigDataFromBinary() and
|
||||
* ConfigManager::writeConfigDataToBinary()can serialize CameraInfo object
|
||||
* to the filesystem and construct CameraInfo instead; this was
|
||||
* evaluated as 10x faster.
|
||||
*/
|
||||
if (!cfgMgr->readConfigDataFromXML()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return cfgMgr;
|
||||
}
|
||||
}
|
||||
|
||||
ConfigManager::CameraInfo::~CameraInfo() {
|
||||
free_camera_metadata(characteristics);
|
||||
|
||||
for (auto&& [tag, val] : cameraMetadata) {
|
||||
switch (tag) {
|
||||
case ANDROID_LENS_DISTORTION:
|
||||
case ANDROID_LENS_POSE_ROTATION:
|
||||
case ANDROID_LENS_POSE_TRANSLATION:
|
||||
case ANDROID_LENS_INTRINSIC_CALIBRATION: {
|
||||
delete[] reinterpret_cast<float*>(val.first);
|
||||
break;
|
||||
}
|
||||
|
||||
case ANDROID_REQUEST_AVAILABLE_CAPABILITIES: {
|
||||
delete[] reinterpret_cast<
|
||||
camera_metadata_enum_android_request_available_capabilities_t*>(val.first);
|
||||
break;
|
||||
}
|
||||
|
||||
case ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS: {
|
||||
delete[] reinterpret_cast<char*>(val.first);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG(WARNING) << "Tag " << std::hex << tag << " is not supported. "
|
||||
<< "Data may be corrupted?";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
155
automotive/evs/aidl/impl/default/src/ConfigManagerUtil.cpp
Normal file
155
automotive/evs/aidl/impl/default/src/ConfigManagerUtil.cpp
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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 "ConfigManagerUtil.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parsedouble.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <system/graphics-base-v1.0.h>
|
||||
|
||||
using ::aidl::android::hardware::automotive::evs::CameraParam;
|
||||
using ::aidl::android::hardware::graphics::common::PixelFormat;
|
||||
|
||||
bool ConfigManagerUtil::convertToEvsCameraParam(const std::string& id, CameraParam& camParam) {
|
||||
std::string trimmed = ConfigManagerUtil::trimString(id);
|
||||
bool success = true;
|
||||
|
||||
if (!trimmed.compare("BRIGHTNESS")) {
|
||||
camParam = CameraParam::BRIGHTNESS;
|
||||
} else if (!trimmed.compare("CONTRAST")) {
|
||||
camParam = CameraParam::CONTRAST;
|
||||
} else if (!trimmed.compare("AUTOGAIN")) {
|
||||
camParam = CameraParam::AUTOGAIN;
|
||||
} else if (!trimmed.compare("GAIN")) {
|
||||
camParam = CameraParam::GAIN;
|
||||
} else if (!trimmed.compare("AUTO_WHITE_BALANCE")) {
|
||||
camParam = CameraParam::AUTO_WHITE_BALANCE;
|
||||
} else if (!trimmed.compare("WHITE_BALANCE_TEMPERATURE")) {
|
||||
camParam = CameraParam::WHITE_BALANCE_TEMPERATURE;
|
||||
} else if (!trimmed.compare("SHARPNESS")) {
|
||||
camParam = CameraParam::SHARPNESS;
|
||||
} else if (!trimmed.compare("AUTO_EXPOSURE")) {
|
||||
camParam = CameraParam::AUTO_EXPOSURE;
|
||||
} else if (!trimmed.compare("ABSOLUTE_EXPOSURE")) {
|
||||
camParam = CameraParam::ABSOLUTE_EXPOSURE;
|
||||
} else if (!trimmed.compare("ABSOLUTE_FOCUS")) {
|
||||
camParam = CameraParam::ABSOLUTE_FOCUS;
|
||||
} else if (!trimmed.compare("AUTO_FOCUS")) {
|
||||
camParam = CameraParam::AUTO_FOCUS;
|
||||
} else if (!trimmed.compare("ABSOLUTE_ZOOM")) {
|
||||
camParam = CameraParam::ABSOLUTE_ZOOM;
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ConfigManagerUtil::convertToPixelFormat(const std::string& in, PixelFormat& out) {
|
||||
std::string trimmed = ConfigManagerUtil::trimString(in);
|
||||
bool success = true;
|
||||
|
||||
if (!trimmed.compare("RGBA_8888")) {
|
||||
out = PixelFormat::RGBA_8888;
|
||||
} else if (!trimmed.compare("YCRCB_420_SP")) {
|
||||
out = PixelFormat::YCRCB_420_SP;
|
||||
} else if (!trimmed.compare("YCBCR_422_I")) {
|
||||
out = PixelFormat::YCBCR_422_I;
|
||||
} else {
|
||||
out = PixelFormat::UNSPECIFIED;
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ConfigManagerUtil::convertToMetadataTag(const char* name, camera_metadata_tag& aTag) {
|
||||
if (!std::strcmp(name, "LENS_DISTORTION")) {
|
||||
aTag = ANDROID_LENS_DISTORTION;
|
||||
} else if (!std::strcmp(name, "LENS_INTRINSIC_CALIBRATION")) {
|
||||
aTag = ANDROID_LENS_INTRINSIC_CALIBRATION;
|
||||
} else if (!std::strcmp(name, "LENS_POSE_ROTATION")) {
|
||||
aTag = ANDROID_LENS_POSE_ROTATION;
|
||||
} else if (!std::strcmp(name, "LENS_POSE_TRANSLATION")) {
|
||||
aTag = ANDROID_LENS_POSE_TRANSLATION;
|
||||
} else if (!std::strcmp(name, "REQUEST_AVAILABLE_CAPABILITIES")) {
|
||||
aTag = ANDROID_REQUEST_AVAILABLE_CAPABILITIES;
|
||||
} else if (!std::strcmp(name, "LOGICAL_MULTI_CAMERA_PHYSICAL_IDS")) {
|
||||
aTag = ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConfigManagerUtil::convertToCameraCapability(
|
||||
const char* name, camera_metadata_enum_android_request_available_capabilities_t& cap) {
|
||||
if (!std::strcmp(name, "DEPTH_OUTPUT")) {
|
||||
cap = ANDROID_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT;
|
||||
} else if (!std::strcmp(name, "LOGICAL_MULTI_CAMERA")) {
|
||||
cap = ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA;
|
||||
} else if (!std::strcmp(name, "MONOCHROME")) {
|
||||
cap = ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME;
|
||||
} else if (!std::strcmp(name, "SECURE_IMAGE_DATA")) {
|
||||
cap = ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float* ConfigManagerUtil::convertFloatArray(const char* sz, const char* vals, size_t& count,
|
||||
const char delimiter) {
|
||||
std::string size_string(sz);
|
||||
std::string value_string(vals);
|
||||
|
||||
if (!android::base::ParseUint(size_string, &count)) {
|
||||
LOG(ERROR) << "Failed to parse " << size_string;
|
||||
return nullptr;
|
||||
}
|
||||
float* result = new float[count];
|
||||
std::stringstream values(value_string);
|
||||
|
||||
int32_t idx = 0;
|
||||
std::string token;
|
||||
while (getline(values, token, delimiter)) {
|
||||
if (!android::base::ParseFloat(token, &result[idx++])) {
|
||||
LOG(WARNING) << "Failed to parse " << token;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string ConfigManagerUtil::trimString(const std::string& src, const std::string& ws) {
|
||||
const auto s = src.find_first_not_of(ws);
|
||||
if (s == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const auto e = src.find_last_not_of(ws);
|
||||
const auto r = e - s + 1;
|
||||
|
||||
return src.substr(s, r);
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// TODO(b/203661081): Remove below lines to disable compiler warnings.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
#define LOG_TAG "DefaultEvsEnumerator"
|
||||
|
||||
#include <DefaultEvsEnumerator.h>
|
||||
|
||||
namespace aidl::android::hardware::automotive::evs::implementation {
|
||||
|
||||
using ::ndk::ScopedAStatus;
|
||||
|
||||
ScopedAStatus DefaultEvsEnumerator::isHardware(bool* flag) {
|
||||
// This returns true always.
|
||||
*flag = true;
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus DefaultEvsEnumerator::openCamera(const std::string& cameraId,
|
||||
const Stream& streamConfig,
|
||||
std::shared_ptr<IEvsCamera>* obj) {
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus DefaultEvsEnumerator::closeCamera(const std::shared_ptr<IEvsCamera>& obj) {
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus DefaultEvsEnumerator::getCameraList(std::vector<CameraDesc>* list) {
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus DefaultEvsEnumerator::getStreamList(const CameraDesc& desc,
|
||||
std::vector<Stream>* _aidl_return) {
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus DefaultEvsEnumerator::openDisplay(int32_t displayId,
|
||||
std::shared_ptr<IEvsDisplay>* obj) {
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus DefaultEvsEnumerator::closeDisplay(const std::shared_ptr<IEvsDisplay>& state) {
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus DefaultEvsEnumerator::getDisplayIdList(std::vector<uint8_t>* list) {
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus DefaultEvsEnumerator::getDisplayState(DisplayState* state) {
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus DefaultEvsEnumerator::registerStatusCallback(
|
||||
const std::shared_ptr<IEvsEnumeratorStatusCallback>& callback) {
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus DefaultEvsEnumerator::openUltrasonicsArray(
|
||||
const std::string& id, std::shared_ptr<IEvsUltrasonicsArray>* obj) {
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus DefaultEvsEnumerator::closeUltrasonicsArray(
|
||||
const std::shared_ptr<IEvsUltrasonicsArray>& obj) {
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus DefaultEvsEnumerator::getUltrasonicsArrayList(
|
||||
std::vector<UltrasonicsArrayDesc>* list) {
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::automotive::evs::implementation
|
||||
|
||||
#pragma clang diagnostic pop
|
552
automotive/evs/aidl/impl/default/src/EvsEnumerator.cpp
Normal file
552
automotive/evs/aidl/impl/default/src/EvsEnumerator.cpp
Normal file
|
@ -0,0 +1,552 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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 "EvsEnumerator.h"
|
||||
|
||||
#include "ConfigManager.h"
|
||||
#include "EvsGlDisplay.h"
|
||||
#include "EvsMockCamera.h"
|
||||
|
||||
#include <aidl/android/hardware/automotive/evs/EvsResult.h>
|
||||
#include <aidl/android/hardware/graphics/common/BufferUsage.h>
|
||||
#include <aidl/android/hardware/graphics/common/PixelFormat.h>
|
||||
#include <cutils/android_filesystem_config.h>
|
||||
|
||||
#include <set>
|
||||
#include <string_view>
|
||||
|
||||
namespace {
|
||||
|
||||
using ::aidl::android::frameworks::automotive::display::ICarDisplayProxy;
|
||||
using ::aidl::android::hardware::graphics::common::BufferUsage;
|
||||
using ::ndk::ScopedAStatus;
|
||||
using std::chrono_literals::operator""s;
|
||||
|
||||
// Constants
|
||||
constexpr std::chrono::seconds kEnumerationTimeout = 10s;
|
||||
constexpr uint64_t kInvalidDisplayId = std::numeric_limits<uint64_t>::max();
|
||||
const std::set<uid_t> kAllowedUids = {AID_AUTOMOTIVE_EVS, AID_SYSTEM, AID_ROOT};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace aidl::android::hardware::automotive::evs::implementation {
|
||||
|
||||
// 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::unordered_map<std::string, EvsEnumerator::CameraRecord> EvsEnumerator::sCameraList;
|
||||
std::mutex EvsEnumerator::sLock;
|
||||
std::condition_variable EvsEnumerator::sCameraSignal;
|
||||
std::unique_ptr<ConfigManager> EvsEnumerator::sConfigManager;
|
||||
std::shared_ptr<ICarDisplayProxy> EvsEnumerator::sDisplayProxy;
|
||||
std::unordered_map<uint8_t, uint64_t> EvsEnumerator::sDisplayPortList;
|
||||
|
||||
EvsEnumerator::ActiveDisplays& EvsEnumerator::mutableActiveDisplays() {
|
||||
static ActiveDisplays active_displays;
|
||||
return active_displays;
|
||||
}
|
||||
|
||||
EvsEnumerator::EvsEnumerator(const std::shared_ptr<ICarDisplayProxy>& proxyService) {
|
||||
LOG(DEBUG) << "EvsEnumerator is created.";
|
||||
|
||||
if (!sConfigManager) {
|
||||
/* loads and initializes ConfigManager in a separate thread */
|
||||
sConfigManager = ConfigManager::Create();
|
||||
}
|
||||
|
||||
if (!sDisplayProxy) {
|
||||
/* sets a car-window service handle */
|
||||
sDisplayProxy = proxyService;
|
||||
}
|
||||
|
||||
// Enumerate existing devices
|
||||
enumerateCameras();
|
||||
mInternalDisplayId = enumerateDisplays();
|
||||
}
|
||||
|
||||
bool EvsEnumerator::checkPermission() {
|
||||
const auto uid = AIBinder_getCallingUid();
|
||||
if (kAllowedUids.find(uid) == kAllowedUids.end()) {
|
||||
LOG(ERROR) << "EVS access denied: "
|
||||
<< "pid = " << AIBinder_getCallingPid() << ", uid = " << uid;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EvsEnumerator::enumerateCameras() {
|
||||
if (!sConfigManager) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto id : sConfigManager->getCameraIdList()) {
|
||||
CameraRecord rec(id.data());
|
||||
std::unique_ptr<ConfigManager::CameraInfo>& pInfo = sConfigManager->getCameraInfo(id);
|
||||
if (pInfo) {
|
||||
uint8_t* ptr = reinterpret_cast<uint8_t*>(pInfo->characteristics);
|
||||
const size_t len = get_camera_metadata_size(pInfo->characteristics);
|
||||
rec.desc.metadata.insert(rec.desc.metadata.end(), ptr, ptr + len);
|
||||
}
|
||||
sCameraList.insert_or_assign(id, std::move(rec));
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t EvsEnumerator::enumerateDisplays() {
|
||||
LOG(INFO) << __FUNCTION__ << ": Starting display enumeration";
|
||||
uint64_t internalDisplayId = kInvalidDisplayId;
|
||||
if (!sDisplayProxy) {
|
||||
LOG(ERROR) << "ICarDisplayProxy is not available!";
|
||||
return internalDisplayId;
|
||||
}
|
||||
|
||||
std::vector<int64_t> displayIds;
|
||||
if (auto status = sDisplayProxy->getDisplayIdList(&displayIds); !status.isOk()) {
|
||||
LOG(ERROR) << "Failed to retrieve a display id list"
|
||||
<< ::android::statusToString(status.getStatus());
|
||||
return internalDisplayId;
|
||||
}
|
||||
|
||||
if (displayIds.size() > 0) {
|
||||
// The first entry of the list is the internal display. See
|
||||
// SurfaceFlinger::getPhysicalDisplayIds() implementation.
|
||||
internalDisplayId = displayIds[0];
|
||||
for (const auto& id : displayIds) {
|
||||
const auto port = id & 0xFF;
|
||||
LOG(INFO) << "Display " << std::hex << id << " is detected on the port, " << port;
|
||||
sDisplayPortList.insert_or_assign(port, id);
|
||||
}
|
||||
}
|
||||
|
||||
LOG(INFO) << "Found " << sDisplayPortList.size() << " displays";
|
||||
return internalDisplayId;
|
||||
}
|
||||
|
||||
// Methods from ::android::hardware::automotive::evs::IEvsEnumerator follow.
|
||||
ScopedAStatus EvsEnumerator::getCameraList(std::vector<CameraDesc>* _aidl_return) {
|
||||
LOG(DEBUG) << __FUNCTION__;
|
||||
if (!checkPermission()) {
|
||||
return ScopedAStatus::fromServiceSpecificError(
|
||||
static_cast<int>(EvsResult::PERMISSION_DENIED));
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(sLock);
|
||||
if (sCameraList.size() < 1) {
|
||||
// No qualified device has been found. Wait until new device is ready,
|
||||
// for 10 seconds.
|
||||
if (!sCameraSignal.wait_for(lock, kEnumerationTimeout,
|
||||
[] { return sCameraList.size() > 0; })) {
|
||||
LOG(DEBUG) << "Timer expired. No new device has been added.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build up a packed array of CameraDesc for return
|
||||
_aidl_return->resize(sCameraList.size());
|
||||
unsigned i = 0;
|
||||
for (const auto& [key, cam] : sCameraList) {
|
||||
(*_aidl_return)[i++] = cam.desc;
|
||||
}
|
||||
|
||||
if (sConfigManager) {
|
||||
// Adding camera groups that represent logical camera devices
|
||||
auto camGroups = sConfigManager->getCameraGroupIdList();
|
||||
for (auto&& id : camGroups) {
|
||||
if (sCameraList.find(id) != sCameraList.end()) {
|
||||
// Already exists in the _aidl_return
|
||||
continue;
|
||||
}
|
||||
|
||||
std::unique_ptr<ConfigManager::CameraGroupInfo>& tempInfo =
|
||||
sConfigManager->getCameraGroupInfo(id);
|
||||
CameraRecord cam(id.data());
|
||||
if (tempInfo) {
|
||||
uint8_t* ptr = reinterpret_cast<uint8_t*>(tempInfo->characteristics);
|
||||
const size_t len = get_camera_metadata_size(tempInfo->characteristics);
|
||||
cam.desc.metadata.insert(cam.desc.metadata.end(), ptr, ptr + len);
|
||||
}
|
||||
|
||||
sCameraList.insert_or_assign(id, cam);
|
||||
_aidl_return->push_back(cam.desc);
|
||||
}
|
||||
}
|
||||
|
||||
// Send back the results
|
||||
LOG(DEBUG) << "Reporting " << sCameraList.size() << " cameras available";
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsEnumerator::getStreamList(const CameraDesc& desc,
|
||||
std::vector<Stream>* _aidl_return) {
|
||||
using AidlPixelFormat = ::aidl::android::hardware::graphics::common::PixelFormat;
|
||||
|
||||
camera_metadata_t* pMetadata = const_cast<camera_metadata_t*>(
|
||||
reinterpret_cast<const camera_metadata_t*>(desc.metadata.data()));
|
||||
camera_metadata_entry_t streamConfig;
|
||||
if (!find_camera_metadata_entry(pMetadata, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
|
||||
&streamConfig)) {
|
||||
const unsigned numStreamConfigs = streamConfig.count / sizeof(StreamConfiguration);
|
||||
_aidl_return->resize(numStreamConfigs);
|
||||
const StreamConfiguration* pCurrentConfig =
|
||||
reinterpret_cast<StreamConfiguration*>(streamConfig.data.i32);
|
||||
for (unsigned i = 0; i < numStreamConfigs; ++i, ++pCurrentConfig) {
|
||||
// Build ::aidl::android::hardware::automotive::evs::Stream from
|
||||
// StreamConfiguration.
|
||||
Stream current = {
|
||||
.id = pCurrentConfig->id,
|
||||
.streamType =
|
||||
pCurrentConfig->type ==
|
||||
ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT
|
||||
? StreamType::INPUT
|
||||
: StreamType::OUTPUT,
|
||||
.width = pCurrentConfig->width,
|
||||
.height = pCurrentConfig->height,
|
||||
.format = static_cast<AidlPixelFormat>(pCurrentConfig->format),
|
||||
.usage = BufferUsage::CAMERA_INPUT,
|
||||
.rotation = Rotation::ROTATION_0,
|
||||
};
|
||||
|
||||
(*_aidl_return)[i] = current;
|
||||
}
|
||||
}
|
||||
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsEnumerator::openCamera(const std::string& id, const Stream& cfg,
|
||||
std::shared_ptr<IEvsCamera>* obj) {
|
||||
LOG(DEBUG) << __FUNCTION__;
|
||||
if (!checkPermission()) {
|
||||
return ScopedAStatus::fromServiceSpecificError(
|
||||
static_cast<int>(EvsResult::PERMISSION_DENIED));
|
||||
}
|
||||
|
||||
// Is this a recognized camera id?
|
||||
CameraRecord* pRecord = findCameraById(id);
|
||||
if (!pRecord) {
|
||||
LOG(ERROR) << id << " does not exist!";
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
|
||||
}
|
||||
|
||||
// Has this camera already been instantiated by another caller?
|
||||
std::shared_ptr<EvsMockCamera> pActiveCamera = pRecord->activeInstance.lock();
|
||||
if (pActiveCamera) {
|
||||
LOG(WARNING) << "Killing previous camera because of new caller";
|
||||
closeCamera(pActiveCamera);
|
||||
}
|
||||
|
||||
// Construct a camera instance for the caller
|
||||
if (!sConfigManager) {
|
||||
pActiveCamera = EvsMockCamera::Create(id.data());
|
||||
} else {
|
||||
pActiveCamera = EvsMockCamera::Create(id.data(), sConfigManager->getCameraInfo(id), &cfg);
|
||||
}
|
||||
|
||||
pRecord->activeInstance = pActiveCamera;
|
||||
if (!pActiveCamera) {
|
||||
LOG(ERROR) << "Failed to create new EvsMockCamera object for " << id;
|
||||
return ScopedAStatus::fromServiceSpecificError(
|
||||
static_cast<int>(EvsResult::UNDERLYING_SERVICE_ERROR));
|
||||
}
|
||||
|
||||
*obj = pActiveCamera;
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsEnumerator::closeCamera(const std::shared_ptr<IEvsCamera>& cameraObj) {
|
||||
LOG(DEBUG) << __FUNCTION__;
|
||||
|
||||
if (!cameraObj) {
|
||||
LOG(ERROR) << "Ignoring call to closeCamera with null camera ptr";
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
|
||||
}
|
||||
|
||||
// Get the camera id so we can find it in our list
|
||||
CameraDesc desc;
|
||||
auto status = cameraObj->getCameraInfo(&desc);
|
||||
if (!status.isOk()) {
|
||||
LOG(ERROR) << "Failed to read a camera descriptor";
|
||||
return ScopedAStatus::fromServiceSpecificError(
|
||||
static_cast<int>(EvsResult::UNDERLYING_SERVICE_ERROR));
|
||||
}
|
||||
auto cameraId = desc.id;
|
||||
closeCamera_impl(cameraObj, cameraId);
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsEnumerator::openDisplay(int32_t id, std::shared_ptr<IEvsDisplay>* displayObj) {
|
||||
LOG(DEBUG) << __FUNCTION__;
|
||||
if (!checkPermission()) {
|
||||
return ScopedAStatus::fromServiceSpecificError(
|
||||
static_cast<int>(EvsResult::PERMISSION_DENIED));
|
||||
}
|
||||
|
||||
auto& displays = mutableActiveDisplays();
|
||||
|
||||
if (auto existing_display_search = displays.popDisplay(id)) {
|
||||
// If we already have a display active, then we need to shut it down so we can
|
||||
// give exclusive access to the new caller.
|
||||
std::shared_ptr<EvsGlDisplay> pActiveDisplay = existing_display_search->displayWeak.lock();
|
||||
if (pActiveDisplay) {
|
||||
LOG(WARNING) << "Killing previous display because of new caller";
|
||||
pActiveDisplay->forceShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new display interface and return it
|
||||
uint64_t targetDisplayId = mInternalDisplayId;
|
||||
auto it = sDisplayPortList.find(id);
|
||||
if (it != sDisplayPortList.end()) {
|
||||
targetDisplayId = it->second;
|
||||
} else {
|
||||
LOG(WARNING) << "No display is available on the port " << static_cast<int32_t>(id)
|
||||
<< ". The main display " << mInternalDisplayId << " will be used instead";
|
||||
}
|
||||
|
||||
// Create a new display interface and return it.
|
||||
std::shared_ptr<EvsGlDisplay> pActiveDisplay =
|
||||
ndk::SharedRefBase::make<EvsGlDisplay>(sDisplayProxy, targetDisplayId);
|
||||
|
||||
if (auto insert_result = displays.tryInsert(id, pActiveDisplay); !insert_result) {
|
||||
LOG(ERROR) << "Display ID " << id << " has been used by another caller.";
|
||||
pActiveDisplay->forceShutdown();
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::RESOURCE_BUSY));
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "Returning new EvsGlDisplay object " << pActiveDisplay.get();
|
||||
*displayObj = pActiveDisplay;
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsEnumerator::closeDisplay(const std::shared_ptr<IEvsDisplay>& obj) {
|
||||
LOG(DEBUG) << __FUNCTION__;
|
||||
|
||||
auto& displays = mutableActiveDisplays();
|
||||
const auto display_search = displays.popDisplay(obj);
|
||||
|
||||
if (!display_search) {
|
||||
LOG(WARNING) << "Ignoring close of previously orphaned display - why did a client steal?";
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
auto pActiveDisplay = display_search->displayWeak.lock();
|
||||
|
||||
if (!pActiveDisplay) {
|
||||
LOG(ERROR) << "Somehow a display is being destroyed "
|
||||
<< "when the enumerator didn't know one existed";
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
|
||||
}
|
||||
|
||||
pActiveDisplay->forceShutdown();
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsEnumerator::getDisplayState(DisplayState* state) {
|
||||
LOG(DEBUG) << __FUNCTION__;
|
||||
if (!checkPermission()) {
|
||||
*state = DisplayState::DEAD;
|
||||
return ScopedAStatus::fromServiceSpecificError(
|
||||
static_cast<int>(EvsResult::PERMISSION_DENIED));
|
||||
}
|
||||
|
||||
// TODO(b/262779341): For now we can just return the state of the 1st display. Need to update
|
||||
// the API later.
|
||||
|
||||
const auto& all_displays = mutableActiveDisplays().getAllDisplays();
|
||||
|
||||
// Do we still have a display object we think should be active?
|
||||
if (all_displays.empty()) {
|
||||
*state = DisplayState::NOT_OPEN;
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
|
||||
}
|
||||
|
||||
std::shared_ptr<IEvsDisplay> pActiveDisplay = all_displays.begin()->second.displayWeak.lock();
|
||||
if (pActiveDisplay) {
|
||||
return pActiveDisplay->getDisplayState(state);
|
||||
} else {
|
||||
*state = DisplayState::NOT_OPEN;
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
|
||||
}
|
||||
}
|
||||
|
||||
ScopedAStatus EvsEnumerator::getDisplayIdList(std::vector<uint8_t>* list) {
|
||||
std::vector<uint8_t>& output = *list;
|
||||
if (sDisplayPortList.size() > 0) {
|
||||
output.resize(sDisplayPortList.size());
|
||||
unsigned i = 0;
|
||||
output[i++] = mInternalDisplayId & 0xFF;
|
||||
for (const auto& [port, id] : sDisplayPortList) {
|
||||
if (mInternalDisplayId != id) {
|
||||
output[i++] = port;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsEnumerator::isHardware(bool* flag) {
|
||||
*flag = true;
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
void EvsEnumerator::notifyDeviceStatusChange(const std::string_view& deviceName,
|
||||
DeviceStatusType type) {
|
||||
std::lock_guard lock(sLock);
|
||||
if (!mCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<DeviceStatus> status{{.id = std::string(deviceName), .status = type}};
|
||||
if (!mCallback->deviceStatusChanged(status).isOk()) {
|
||||
LOG(WARNING) << "Failed to notify a device status change, name = " << deviceName
|
||||
<< ", type = " << static_cast<int>(type);
|
||||
}
|
||||
}
|
||||
|
||||
ScopedAStatus EvsEnumerator::registerStatusCallback(
|
||||
const std::shared_ptr<IEvsEnumeratorStatusCallback>& callback) {
|
||||
std::lock_guard lock(sLock);
|
||||
if (mCallback) {
|
||||
LOG(INFO) << "Replacing an existing device status callback";
|
||||
}
|
||||
mCallback = callback;
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
void EvsEnumerator::closeCamera_impl(const std::shared_ptr<IEvsCamera>& pCamera,
|
||||
const std::string& cameraId) {
|
||||
// Find the named camera
|
||||
CameraRecord* pRecord = findCameraById(cameraId);
|
||||
|
||||
// Is the display being destroyed actually the one we think is active?
|
||||
if (!pRecord) {
|
||||
LOG(ERROR) << "Asked to close a camera whose name isn't recognized";
|
||||
} else {
|
||||
std::shared_ptr<EvsMockCamera> pActiveCamera = pRecord->activeInstance.lock();
|
||||
if (!pActiveCamera) {
|
||||
LOG(WARNING) << "Somehow a camera is being destroyed "
|
||||
<< "when the enumerator didn't know one existed";
|
||||
} else if (pActiveCamera != pCamera) {
|
||||
// This can happen if the camera was aggressively reopened,
|
||||
// orphaning this previous instance
|
||||
LOG(WARNING) << "Ignoring close of previously orphaned camera "
|
||||
<< "- why did a client steal?";
|
||||
} else {
|
||||
// Shutdown the active camera
|
||||
pActiveCamera->shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
EvsEnumerator::CameraRecord* EvsEnumerator::findCameraById(const std::string& cameraId) {
|
||||
// Find the named camera
|
||||
auto found = sCameraList.find(cameraId);
|
||||
if (found != sCameraList.end()) {
|
||||
// Found a match!
|
||||
return &found->second;
|
||||
}
|
||||
|
||||
// We didn't find a match
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::optional<EvsEnumerator::ActiveDisplays::DisplayInfo> EvsEnumerator::ActiveDisplays::popDisplay(
|
||||
int32_t id) {
|
||||
std::lock_guard lck(mMutex);
|
||||
const auto search = mIdToDisplay.find(id);
|
||||
if (search == mIdToDisplay.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto display_info = search->second;
|
||||
mIdToDisplay.erase(search);
|
||||
mDisplayToId.erase(display_info.internalDisplayRawAddr);
|
||||
return display_info;
|
||||
}
|
||||
|
||||
std::optional<EvsEnumerator::ActiveDisplays::DisplayInfo> EvsEnumerator::ActiveDisplays::popDisplay(
|
||||
const std::shared_ptr<IEvsDisplay>& display) {
|
||||
const auto display_ptr_val = reinterpret_cast<uintptr_t>(display.get());
|
||||
std::lock_guard lck(mMutex);
|
||||
const auto display_to_id_search = mDisplayToId.find(display_ptr_val);
|
||||
if (display_to_id_search == mDisplayToId.end()) {
|
||||
LOG(ERROR) << "Unknown display.";
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto id = display_to_id_search->second;
|
||||
const auto id_to_display_search = mIdToDisplay.find(id);
|
||||
mDisplayToId.erase(display_to_id_search);
|
||||
if (id_to_display_search == mIdToDisplay.end()) {
|
||||
LOG(ERROR) << "No correspsonding ID for the display, probably orphaned.";
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto display_info = id_to_display_search->second;
|
||||
mIdToDisplay.erase(id);
|
||||
return display_info;
|
||||
}
|
||||
|
||||
std::unordered_map<int32_t, EvsEnumerator::ActiveDisplays::DisplayInfo>
|
||||
EvsEnumerator::ActiveDisplays::getAllDisplays() {
|
||||
std::lock_guard lck(mMutex);
|
||||
auto id_to_display_map_copy = mIdToDisplay;
|
||||
return id_to_display_map_copy;
|
||||
}
|
||||
|
||||
bool EvsEnumerator::ActiveDisplays::tryInsert(int32_t id,
|
||||
const std::shared_ptr<EvsGlDisplay>& display) {
|
||||
std::lock_guard lck(mMutex);
|
||||
const auto display_ptr_val = reinterpret_cast<uintptr_t>(display.get());
|
||||
|
||||
auto id_to_display_insert_result =
|
||||
mIdToDisplay.emplace(id, DisplayInfo{
|
||||
.id = id,
|
||||
.displayWeak = display,
|
||||
.internalDisplayRawAddr = display_ptr_val,
|
||||
});
|
||||
if (!id_to_display_insert_result.second) {
|
||||
return false;
|
||||
}
|
||||
auto display_to_id_insert_result = mDisplayToId.emplace(display_ptr_val, id);
|
||||
if (!display_to_id_insert_result.second) {
|
||||
mIdToDisplay.erase(id);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ScopedAStatus EvsEnumerator::getUltrasonicsArrayList(
|
||||
[[maybe_unused]] std::vector<UltrasonicsArrayDesc>* list) {
|
||||
// TODO(b/149874793): Add implementation for EVS Manager and Sample driver
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsEnumerator::openUltrasonicsArray(
|
||||
[[maybe_unused]] const std::string& id,
|
||||
[[maybe_unused]] std::shared_ptr<IEvsUltrasonicsArray>* obj) {
|
||||
// TODO(b/149874793): Add implementation for EVS Manager and Sample driver
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsEnumerator::closeUltrasonicsArray(
|
||||
[[maybe_unused]] const std::shared_ptr<IEvsUltrasonicsArray>& obj) {
|
||||
// TODO(b/149874793): Add implementation for EVS Manager and Sample driver
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::automotive::evs::implementation
|
417
automotive/evs/aidl/impl/default/src/EvsGlDisplay.cpp
Normal file
417
automotive/evs/aidl/impl/default/src/EvsGlDisplay.cpp
Normal file
|
@ -0,0 +1,417 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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 "EvsGlDisplay.h"
|
||||
|
||||
#include <aidl/android/hardware/automotive/evs/EvsResult.h>
|
||||
#include <aidl/android/hardware/graphics/common/BufferUsage.h>
|
||||
#include <aidl/android/hardware/graphics/common/PixelFormat.h>
|
||||
#include <aidlcommonsupport/NativeHandle.h>
|
||||
#include <android-base/thread_annotations.h>
|
||||
#include <linux/time.h>
|
||||
#include <ui/DisplayMode.h>
|
||||
#include <ui/DisplayState.h>
|
||||
#include <ui/GraphicBufferAllocator.h>
|
||||
#include <ui/GraphicBufferMapper.h>
|
||||
#include <utils/SystemClock.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace {
|
||||
|
||||
using ::aidl::android::frameworks::automotive::display::ICarDisplayProxy;
|
||||
using ::aidl::android::hardware::graphics::common::BufferUsage;
|
||||
using ::aidl::android::hardware::graphics::common::PixelFormat;
|
||||
using ::android::base::ScopedLockAssertion;
|
||||
using ::ndk::ScopedAStatus;
|
||||
|
||||
constexpr auto kTimeout = std::chrono::seconds(1);
|
||||
|
||||
bool debugFirstFrameDisplayed = false;
|
||||
|
||||
int generateFingerPrint(buffer_handle_t handle) {
|
||||
return static_cast<int>(reinterpret_cast<long>(handle) & 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace aidl::android::hardware::automotive::evs::implementation {
|
||||
|
||||
EvsGlDisplay::EvsGlDisplay(const std::shared_ptr<ICarDisplayProxy>& pDisplayProxy,
|
||||
uint64_t displayId)
|
||||
: mDisplayId(displayId), mDisplayProxy(pDisplayProxy) {
|
||||
LOG(DEBUG) << "EvsGlDisplay instantiated";
|
||||
|
||||
// Set up our self description
|
||||
// NOTE: These are arbitrary values chosen for testing
|
||||
mInfo.id = std::to_string(displayId);
|
||||
mInfo.vendorFlags = 3870;
|
||||
|
||||
// Start a thread to render images on this display
|
||||
{
|
||||
std::lock_guard lock(mLock);
|
||||
mState = RUN;
|
||||
}
|
||||
mRenderThread = std::thread([this]() { renderFrames(); });
|
||||
}
|
||||
|
||||
EvsGlDisplay::~EvsGlDisplay() {
|
||||
LOG(DEBUG) << "EvsGlDisplay being destroyed";
|
||||
forceShutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* This gets called if another caller "steals" ownership of the display
|
||||
*/
|
||||
void EvsGlDisplay::forceShutdown() {
|
||||
LOG(DEBUG) << "EvsGlDisplay forceShutdown";
|
||||
{
|
||||
std::lock_guard lock(mLock);
|
||||
|
||||
// If the buffer isn't being held by a remote client, release it now as an
|
||||
// optimization to release the resources more quickly than the destructor might
|
||||
// get called.
|
||||
if (mBuffer.handle != nullptr) {
|
||||
// Report if we're going away while a buffer is outstanding
|
||||
if (mBufferBusy || mState == RUN) {
|
||||
LOG(ERROR) << "EvsGlDisplay going down while client is holding a buffer";
|
||||
}
|
||||
mState = STOPPING;
|
||||
}
|
||||
|
||||
// Put this object into an unrecoverable error state since somebody else
|
||||
// is going to own the display now.
|
||||
mRequestedState = DisplayState::DEAD;
|
||||
}
|
||||
mBufferReadyToRender.notify_all();
|
||||
|
||||
if (mRenderThread.joinable()) {
|
||||
mRenderThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize GL in the context of a caller's thread and prepare a graphic
|
||||
* buffer to use.
|
||||
*/
|
||||
bool EvsGlDisplay::initializeGlContextLocked() {
|
||||
// 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)) {
|
||||
// Report the failure
|
||||
LOG(ERROR) << "Failed to initialize GL display";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Assemble the buffer description we'll use for our render target
|
||||
static_assert(::aidl::android::hardware::graphics::common::PixelFormat::RGBA_8888 ==
|
||||
static_cast<::aidl::android::hardware::graphics::common::PixelFormat>(
|
||||
HAL_PIXEL_FORMAT_RGBA_8888));
|
||||
mBuffer.description = {
|
||||
.width = static_cast<int>(mGlWrapper.getWidth()),
|
||||
.height = static_cast<int>(mGlWrapper.getHeight()),
|
||||
.layers = 1,
|
||||
.format = PixelFormat::RGBA_8888,
|
||||
// FIXME: Below line is not using
|
||||
// ::aidl::android::hardware::graphics::common::BufferUsage because
|
||||
// BufferUsage enum does not support a bitwise-OR operation; they
|
||||
// should be BufferUsage::GPU_RENDER_TARGET |
|
||||
// BufferUsage::COMPOSER_OVERLAY
|
||||
.usage = static_cast<BufferUsage>(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER),
|
||||
};
|
||||
|
||||
::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
|
||||
uint32_t stride = static_cast<uint32_t>(mBuffer.description.stride);
|
||||
buffer_handle_t handle = nullptr;
|
||||
const ::android::status_t result =
|
||||
alloc.allocate(mBuffer.description.width, mBuffer.description.height,
|
||||
static_cast<::android::PixelFormat>(mBuffer.description.format),
|
||||
mBuffer.description.layers,
|
||||
static_cast<uint64_t>(mBuffer.description.usage), &handle, &stride,
|
||||
/* requestorName= */ "EvsGlDisplay");
|
||||
mBuffer.description.stride = stride;
|
||||
mBuffer.fingerprint = generateFingerPrint(mBuffer.handle);
|
||||
if (result != ::android::NO_ERROR) {
|
||||
LOG(ERROR) << "Error " << result << " allocating " << mBuffer.description.width << " x "
|
||||
<< mBuffer.description.height << " graphics buffer.";
|
||||
mGlWrapper.shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
mBuffer.handle = handle;
|
||||
if (mBuffer.handle == nullptr) {
|
||||
LOG(ERROR) << "We didn't get a buffer handle back from the allocator";
|
||||
mGlWrapper.shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "Allocated new buffer " << mBuffer.handle << " with stride "
|
||||
<< mBuffer.description.stride;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method runs in a separate thread and renders the contents of the buffer.
|
||||
*/
|
||||
void EvsGlDisplay::renderFrames() {
|
||||
{
|
||||
std::lock_guard lock(mLock);
|
||||
|
||||
if (!initializeGlContextLocked()) {
|
||||
LOG(ERROR) << "Failed to initialize GL context";
|
||||
return;
|
||||
}
|
||||
|
||||
// Display buffer is ready.
|
||||
mBufferBusy = false;
|
||||
}
|
||||
mBufferReadyToUse.notify_all();
|
||||
|
||||
while (true) {
|
||||
{
|
||||
std::unique_lock lock(mLock);
|
||||
ScopedLockAssertion lock_assertion(mLock);
|
||||
mBufferReadyToRender.wait(
|
||||
lock, [this]() REQUIRES(mLock) { return mBufferReady || mState != RUN; });
|
||||
if (mState != RUN) {
|
||||
LOG(DEBUG) << "A rendering thread is stopping";
|
||||
break;
|
||||
}
|
||||
mBufferReady = false;
|
||||
}
|
||||
|
||||
// Update the texture contents with the provided data
|
||||
if (!mGlWrapper.updateImageTexture(mBuffer.handle, mBuffer.description)) {
|
||||
LOG(WARNING) << "Failed to update the image texture";
|
||||
continue;
|
||||
}
|
||||
|
||||
// Put the image on the screen
|
||||
mGlWrapper.renderImageToScreen();
|
||||
if (!debugFirstFrameDisplayed) {
|
||||
LOG(DEBUG) << "EvsFirstFrameDisplayTiming start time: " << ::android::elapsedRealtime()
|
||||
<< " ms.";
|
||||
debugFirstFrameDisplayed = true;
|
||||
}
|
||||
|
||||
// Mark current frame is consumed.
|
||||
{
|
||||
std::lock_guard lock(mLock);
|
||||
mBufferBusy = false;
|
||||
}
|
||||
mBufferDone.notify_all();
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "A rendering thread is stopped.";
|
||||
|
||||
// Drop the graphics buffer we've been using
|
||||
::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
|
||||
alloc.free(mBuffer.handle);
|
||||
mBuffer.handle = nullptr;
|
||||
|
||||
mGlWrapper.hideWindow(mDisplayProxy, mDisplayId);
|
||||
mGlWrapper.shutdown();
|
||||
|
||||
std::lock_guard lock(mLock);
|
||||
mState = STOPPED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns basic information about the EVS display provided by the system.
|
||||
* See the description of the DisplayDesc structure for details.
|
||||
*/
|
||||
ScopedAStatus EvsGlDisplay::getDisplayInfo(DisplayDesc* _aidl_return) {
|
||||
if (!mDisplayProxy) {
|
||||
return ::ndk::ScopedAStatus::fromServiceSpecificError(
|
||||
static_cast<int>(EvsResult::UNDERLYING_SERVICE_ERROR));
|
||||
}
|
||||
|
||||
::aidl::android::frameworks::automotive::display::DisplayDesc proxyDisplay;
|
||||
auto status = mDisplayProxy->getDisplayInfo(mDisplayId, &proxyDisplay);
|
||||
if (!status.isOk()) {
|
||||
return ::ndk::ScopedAStatus::fromServiceSpecificError(
|
||||
static_cast<int>(EvsResult::UNDERLYING_SERVICE_ERROR));
|
||||
}
|
||||
|
||||
_aidl_return->width = proxyDisplay.width;
|
||||
_aidl_return->height = proxyDisplay.height;
|
||||
_aidl_return->orientation = static_cast<Rotation>(proxyDisplay.orientation);
|
||||
_aidl_return->id = mInfo.id; // FIXME: what should be ID here?
|
||||
_aidl_return->vendorFlags = mInfo.vendorFlags;
|
||||
return ::ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clients may set the display state to express their desired state.
|
||||
* The HAL implementation must gracefully accept a request for any state
|
||||
* while in any other state, although the response may be to ignore the request.
|
||||
* The display is defined to start in the NOT_VISIBLE state upon initialization.
|
||||
* The client is then expected to request the VISIBLE_ON_NEXT_FRAME state, and
|
||||
* then begin providing video. When the display is no longer required, the client
|
||||
* is expected to request the NOT_VISIBLE state after passing the last video frame.
|
||||
*/
|
||||
ScopedAStatus EvsGlDisplay::setDisplayState(DisplayState state) {
|
||||
LOG(DEBUG) << __FUNCTION__;
|
||||
std::lock_guard lock(mLock);
|
||||
|
||||
if (mRequestedState == DisplayState::DEAD) {
|
||||
// This object no longer owns the display -- it's been superceeded!
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
|
||||
}
|
||||
|
||||
// Ensure we recognize the requested state so we don't go off the rails
|
||||
static constexpr ::ndk::enum_range<DisplayState> kDisplayStateRange;
|
||||
if (std::find(kDisplayStateRange.begin(), kDisplayStateRange.end(), state) ==
|
||||
kDisplayStateRange.end()) {
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
|
||||
}
|
||||
|
||||
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 ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* The HAL implementation should report the actual current state, which might
|
||||
* transiently differ from the most recently requested state. Note, however, that
|
||||
* the logic responsible for changing display states should generally live above
|
||||
* the device layer, making it undesirable for the HAL implementation to
|
||||
* spontaneously change display states.
|
||||
*/
|
||||
ScopedAStatus EvsGlDisplay::getDisplayState(DisplayState* _aidl_return) {
|
||||
LOG(DEBUG) << __FUNCTION__;
|
||||
std::lock_guard lock(mLock);
|
||||
*_aidl_return = mRequestedState;
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* must be returned via a call to returnTargetBufferForDisplay() even if the
|
||||
* display is no longer visible.
|
||||
*/
|
||||
ScopedAStatus EvsGlDisplay::getTargetBuffer(BufferDesc* _aidl_return) {
|
||||
LOG(DEBUG) << __FUNCTION__;
|
||||
std::unique_lock lock(mLock);
|
||||
ScopedLockAssertion lock_assertion(mLock);
|
||||
if (mRequestedState == DisplayState::DEAD) {
|
||||
LOG(ERROR) << "Rejecting buffer request from object that lost ownership of the display.";
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
|
||||
}
|
||||
|
||||
// If we don't already have a buffer, allocate one now
|
||||
// mBuffer.memHandle is a type of buffer_handle_t, which is equal to
|
||||
// native_handle_t*.
|
||||
mBufferReadyToUse.wait(lock, [this]() REQUIRES(mLock) { return !mBufferBusy; });
|
||||
|
||||
// Do we have a frame available?
|
||||
if (mBufferBusy) {
|
||||
// This means either we have a 2nd client trying to compete for buffers
|
||||
// (an unsupported mode of operation) or else the client hasn't returned
|
||||
// a previously issued buffer yet (they're behaving badly).
|
||||
// NOTE: We have to make the callback even if we have nothing to provide
|
||||
LOG(ERROR) << "getTargetBuffer called while no buffers available.";
|
||||
return ScopedAStatus::fromServiceSpecificError(
|
||||
static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE));
|
||||
}
|
||||
|
||||
// Mark our buffer as busy
|
||||
mBufferBusy = true;
|
||||
|
||||
// Send the buffer to the client
|
||||
LOG(VERBOSE) << "Providing display buffer handle " << mBuffer.handle;
|
||||
|
||||
BufferDesc bufferDescToSend = {
|
||||
.buffer =
|
||||
{
|
||||
.handle = std::move(::android::dupToAidl(mBuffer.handle)),
|
||||
.description = mBuffer.description,
|
||||
},
|
||||
.pixelSizeBytes = 4, // RGBA_8888 is 4-byte-per-pixel format
|
||||
.bufferId = mBuffer.fingerprint,
|
||||
};
|
||||
*_aidl_return = std::move(bufferDescToSend);
|
||||
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
ScopedAStatus EvsGlDisplay::returnTargetBufferForDisplay(const BufferDesc& buffer) {
|
||||
LOG(VERBOSE) << __FUNCTION__;
|
||||
std::unique_lock lock(mLock);
|
||||
ScopedLockAssertion lock_assertion(mLock);
|
||||
|
||||
// Nobody should call us with a null handle
|
||||
if (buffer.buffer.handle.fds.size() < 1) {
|
||||
LOG(ERROR) << __FUNCTION__ << " called without a valid buffer handle.";
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
|
||||
}
|
||||
if (buffer.bufferId != mBuffer.fingerprint) {
|
||||
LOG(ERROR) << "Got an unrecognized frame returned.";
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
|
||||
}
|
||||
if (!mBufferBusy) {
|
||||
LOG(ERROR) << "A frame was returned with no outstanding frames.";
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
|
||||
}
|
||||
|
||||
// If we've been displaced by another owner of the display, then we can't do anything else
|
||||
if (mRequestedState == DisplayState::DEAD) {
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
|
||||
}
|
||||
|
||||
// If we were waiting for a new frame, this is it!
|
||||
if (mRequestedState == DisplayState::VISIBLE_ON_NEXT_FRAME) {
|
||||
mRequestedState = DisplayState::VISIBLE;
|
||||
mGlWrapper.showWindow(mDisplayProxy, mDisplayId);
|
||||
}
|
||||
|
||||
// Validate we're in an expected state
|
||||
if (mRequestedState != DisplayState::VISIBLE) {
|
||||
// Not sure why a client would send frames back when we're not visible.
|
||||
LOG(WARNING) << "Got a frame returned while not visible - ignoring.";
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
mBufferReady = true;
|
||||
mBufferReadyToRender.notify_all();
|
||||
|
||||
if (!mBufferDone.wait_for(lock, kTimeout, [this]() REQUIRES(mLock) { return !mBufferBusy; })) {
|
||||
return ScopedAStatus::fromServiceSpecificError(
|
||||
static_cast<int>(EvsResult::UNDERLYING_SERVICE_ERROR));
|
||||
}
|
||||
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::automotive::evs::implementation
|
735
automotive/evs/aidl/impl/default/src/EvsMockCamera.cpp
Normal file
735
automotive/evs/aidl/impl/default/src/EvsMockCamera.cpp
Normal file
|
@ -0,0 +1,735 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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 "EvsMockCamera.h"
|
||||
#include "ConfigManager.h"
|
||||
#include "EvsEnumerator.h"
|
||||
|
||||
#include <aidlcommonsupport/NativeHandle.h>
|
||||
#include <ui/GraphicBufferAllocator.h>
|
||||
#include <ui/GraphicBufferMapper.h>
|
||||
#include <utils/SystemClock.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace {
|
||||
|
||||
using ::aidl::android::hardware::graphics::common::BufferUsage;
|
||||
using ::ndk::ScopedAStatus;
|
||||
|
||||
// Arbitrary limit on number of graphics buffers allowed to be allocated
|
||||
// Safeguards against unreasonable resource consumption and provides a testable limit
|
||||
constexpr unsigned kMaxBuffersInFlight = 100;
|
||||
|
||||
// Minimum number of buffers to run a video stream
|
||||
constexpr int kMinimumBuffersInFlight = 1;
|
||||
|
||||
// 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 size_t kNumColors = sizeof(kColors) / sizeof(kColors[0]);
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace aidl::android::hardware::automotive::evs::implementation {
|
||||
|
||||
EvsMockCamera::EvsMockCamera([[maybe_unused]] Sigil sigil, const char* id,
|
||||
std::unique_ptr<ConfigManager::CameraInfo>& camInfo)
|
||||
: mFramesAllowed(0), mFramesInUse(0), mStreamState(STOPPED), mCameraInfo(camInfo) {
|
||||
LOG(DEBUG) << __FUNCTION__;
|
||||
|
||||
/* set a camera id */
|
||||
mDescription.id = id;
|
||||
|
||||
/* set camera metadata */
|
||||
if (camInfo) {
|
||||
uint8_t* ptr = reinterpret_cast<uint8_t*>(camInfo->characteristics);
|
||||
const size_t len = get_camera_metadata_size(camInfo->characteristics);
|
||||
mDescription.metadata.insert(mDescription.metadata.end(), ptr, ptr + len);
|
||||
}
|
||||
|
||||
// Initialize parameters.
|
||||
initializeParameters();
|
||||
}
|
||||
|
||||
EvsMockCamera::~EvsMockCamera() {
|
||||
LOG(DEBUG) << __FUNCTION__;
|
||||
shutdown();
|
||||
}
|
||||
|
||||
void EvsMockCamera::initializeParameters() {
|
||||
mParams.emplace(
|
||||
CameraParam::BRIGHTNESS,
|
||||
new CameraParameterDesc(/* min= */ 0, /* max= */ 255, /* step= */ 1, /* value= */ 255));
|
||||
mParams.emplace(
|
||||
CameraParam::CONTRAST,
|
||||
new CameraParameterDesc(/* min= */ 0, /* max= */ 255, /* step= */ 1, /* value= */ 255));
|
||||
mParams.emplace(
|
||||
CameraParam::SHARPNESS,
|
||||
new CameraParameterDesc(/* min= */ 0, /* max= */ 255, /* step= */ 1, /* value= */ 255));
|
||||
}
|
||||
|
||||
// This gets called if another caller "steals" ownership of the camera
|
||||
void EvsMockCamera::shutdown() {
|
||||
LOG(DEBUG) << __FUNCTION__;
|
||||
|
||||
// Make sure our output stream is cleaned up
|
||||
// (It really should be already)
|
||||
stopVideoStream_impl();
|
||||
|
||||
// Claim the lock while we work on internal state
|
||||
std::lock_guard lock(mAccessLock);
|
||||
|
||||
// Drop all the graphics buffers we've been using
|
||||
if (mBuffers.size() > 0) {
|
||||
::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
|
||||
for (auto&& rec : mBuffers) {
|
||||
if (rec.inUse) {
|
||||
LOG(WARNING) << "WARNING: releasing a buffer remotely owned.";
|
||||
}
|
||||
alloc.free(rec.handle);
|
||||
rec.handle = nullptr;
|
||||
}
|
||||
mBuffers.clear();
|
||||
}
|
||||
|
||||
// Put this object into an unrecoverable error state since somebody else
|
||||
// is going to own the underlying camera now
|
||||
mStreamState = DEAD;
|
||||
}
|
||||
|
||||
// Methods from ::aidl::android::hardware::automotive::evs::IEvsCamera follow.
|
||||
ScopedAStatus EvsMockCamera::getCameraInfo(CameraDesc* _aidl_return) {
|
||||
LOG(DEBUG) << __FUNCTION__;
|
||||
|
||||
// Send back our self description
|
||||
*_aidl_return = mDescription;
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsMockCamera::setMaxFramesInFlight(int32_t bufferCount) {
|
||||
LOG(DEBUG) << __FUNCTION__ << ", bufferCount = " << bufferCount;
|
||||
;
|
||||
|
||||
std::lock_guard lock(mAccessLock);
|
||||
|
||||
// If we've been displaced by another owner of the camera, then we can't do anything else
|
||||
if (mStreamState == DEAD) {
|
||||
LOG(ERROR) << "Ignoring setMaxFramesInFlight call when camera has been lost.";
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
|
||||
}
|
||||
|
||||
// We cannot function without at least one video buffer to send data
|
||||
if (bufferCount < 1) {
|
||||
LOG(ERROR) << "Ignoring setMaxFramesInFlight with less than one buffer requested.";
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
|
||||
}
|
||||
|
||||
// Update our internal state
|
||||
if (!setAvailableFrames_Locked(bufferCount)) {
|
||||
LOG(ERROR) << "Failed to adjust the maximum number of frames in flight.";
|
||||
return ScopedAStatus::fromServiceSpecificError(
|
||||
static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE));
|
||||
}
|
||||
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsMockCamera::startVideoStream(const std::shared_ptr<IEvsCameraStream>& cb) {
|
||||
LOG(DEBUG) << __FUNCTION__;
|
||||
|
||||
if (!cb) {
|
||||
LOG(ERROR) << "A given stream callback is invalid.";
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
|
||||
}
|
||||
|
||||
std::lock_guard lock(mAccessLock);
|
||||
|
||||
// If we've been displaced by another owner of the camera, then we can't do anything else
|
||||
if (mStreamState == DEAD) {
|
||||
LOG(ERROR) << "Ignoring startVideoStream call when camera has been lost.";
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
|
||||
}
|
||||
|
||||
if (mStreamState != STOPPED) {
|
||||
LOG(ERROR) << "Ignoring startVideoStream call when a stream is already running.";
|
||||
return ScopedAStatus::fromServiceSpecificError(
|
||||
static_cast<int>(EvsResult::STREAM_ALREADY_RUNNING));
|
||||
}
|
||||
|
||||
// If the client never indicated otherwise, configure ourselves for a single streaming buffer
|
||||
if (mFramesAllowed < kMinimumBuffersInFlight &&
|
||||
!setAvailableFrames_Locked(kMinimumBuffersInFlight)) {
|
||||
LOG(ERROR) << "Failed to start stream because we couldn't get a graphics buffer";
|
||||
return ScopedAStatus::fromServiceSpecificError(
|
||||
static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE));
|
||||
}
|
||||
|
||||
// Record the user's callback for use when we have a frame ready
|
||||
mStream = cb;
|
||||
|
||||
// Start the frame generation thread
|
||||
mStreamState = RUNNING;
|
||||
mCaptureThread = std::thread([this]() { generateFrames(); });
|
||||
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsMockCamera::doneWithFrame(const std::vector<BufferDesc>& list) {
|
||||
std::lock_guard lock(mAccessLock);
|
||||
for (const auto& desc : list) {
|
||||
returnBufferLocked(desc.bufferId);
|
||||
}
|
||||
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsMockCamera::stopVideoStream() {
|
||||
LOG(DEBUG) << __FUNCTION__;
|
||||
return stopVideoStream_impl();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsMockCamera::stopVideoStream_impl() {
|
||||
std::unique_lock lock(mAccessLock);
|
||||
|
||||
if (mStreamState != RUNNING) {
|
||||
// Safely return here because a stream is not running.
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
// 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
|
||||
LOG(DEBUG) << "Waiting for stream thread to end...";
|
||||
lock.unlock();
|
||||
if (mCaptureThread.joinable()) {
|
||||
mCaptureThread.join();
|
||||
}
|
||||
lock.lock();
|
||||
|
||||
mStreamState = STOPPED;
|
||||
mStream = nullptr;
|
||||
LOG(DEBUG) << "Stream marked STOPPED.";
|
||||
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsMockCamera::getExtendedInfo(int32_t opaqueIdentifier,
|
||||
std::vector<uint8_t>* opaqueValue) {
|
||||
const auto it = mExtInfo.find(opaqueIdentifier);
|
||||
if (it == mExtInfo.end()) {
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
|
||||
} else {
|
||||
*opaqueValue = mExtInfo[opaqueIdentifier];
|
||||
}
|
||||
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsMockCamera::setExtendedInfo(int32_t opaqueIdentifier,
|
||||
const std::vector<uint8_t>& opaqueValue) {
|
||||
mExtInfo.insert_or_assign(opaqueIdentifier, opaqueValue);
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsMockCamera::getPhysicalCameraInfo([[maybe_unused]] const std::string& id,
|
||||
CameraDesc* _aidl_return) {
|
||||
LOG(DEBUG) << __FUNCTION__;
|
||||
|
||||
// This method works exactly same as getCameraInfo() in EVS HW module.
|
||||
*_aidl_return = mDescription;
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsMockCamera::pauseVideoStream() {
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
|
||||
}
|
||||
|
||||
ScopedAStatus EvsMockCamera::resumeVideoStream() {
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
|
||||
}
|
||||
|
||||
ScopedAStatus EvsMockCamera::setPrimaryClient() {
|
||||
/* Because EVS HW module reference implementation expects a single client at
|
||||
* a time, this returns a success code always.
|
||||
*/
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsMockCamera::forcePrimaryClient(const std::shared_ptr<IEvsDisplay>&) {
|
||||
/* Because EVS HW module reference implementation expects a single client at
|
||||
* a time, this returns a success code always.
|
||||
*/
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsMockCamera::unsetPrimaryClient() {
|
||||
/* Because EVS HW module reference implementation expects a single client at
|
||||
* a time, there is no chance that this is called by the secondary client and
|
||||
* therefore returns a success code always.
|
||||
*/
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsMockCamera::getParameterList(std::vector<CameraParam>* _aidl_return) {
|
||||
if (mCameraInfo) {
|
||||
_aidl_return->resize(mCameraInfo->controls.size());
|
||||
auto idx = 0;
|
||||
for (auto& [name, range] : mCameraInfo->controls) {
|
||||
(*_aidl_return)[idx++] = name;
|
||||
}
|
||||
}
|
||||
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsMockCamera::getIntParameterRange(CameraParam id, ParameterRange* _aidl_return) {
|
||||
auto it = mParams.find(id);
|
||||
if (it == mParams.end()) {
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
|
||||
}
|
||||
|
||||
_aidl_return->min = it->second->range.min;
|
||||
_aidl_return->max = it->second->range.max;
|
||||
_aidl_return->step = it->second->range.step;
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsMockCamera::setIntParameter(CameraParam id, int32_t value,
|
||||
std::vector<int32_t>* effectiveValue) {
|
||||
auto it = mParams.find(id);
|
||||
if (it == mParams.end()) {
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
|
||||
}
|
||||
|
||||
// Rounding down to the closest value.
|
||||
int32_t candidate = value / it->second->range.step * it->second->range.step;
|
||||
if (candidate < it->second->range.min || candidate > it->second->range.max) {
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
|
||||
}
|
||||
|
||||
it->second->value = candidate;
|
||||
effectiveValue->push_back(candidate);
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsMockCamera::getIntParameter(CameraParam id, std::vector<int32_t>* value) {
|
||||
auto it = mParams.find(id);
|
||||
if (it == mParams.end()) {
|
||||
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
|
||||
}
|
||||
|
||||
value->push_back(it->second->value);
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus EvsMockCamera::importExternalBuffers(const std::vector<BufferDesc>& buffers,
|
||||
int32_t* _aidl_return) {
|
||||
size_t numBuffersToAdd = buffers.size();
|
||||
if (numBuffersToAdd < 1) {
|
||||
LOG(DEBUG) << "Ignoring a request to import external buffers with an empty list.";
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
std::lock_guard lock(mAccessLock);
|
||||
if (numBuffersToAdd > (kMaxBuffersInFlight - mFramesAllowed)) {
|
||||
numBuffersToAdd -= (kMaxBuffersInFlight - mFramesAllowed);
|
||||
LOG(WARNING) << "Exceed the limit on the number of buffers. " << numBuffersToAdd
|
||||
<< " buffers will be imported only.";
|
||||
}
|
||||
|
||||
::android::GraphicBufferMapper& mapper = ::android::GraphicBufferMapper::get();
|
||||
const size_t before = mFramesAllowed;
|
||||
for (size_t i = 0; i < numBuffersToAdd; ++i) {
|
||||
auto& b = buffers[i];
|
||||
const AHardwareBuffer_Desc* pDesc =
|
||||
reinterpret_cast<const AHardwareBuffer_Desc*>(&b.buffer.description);
|
||||
|
||||
buffer_handle_t handleToImport = ::android::dupFromAidl(b.buffer.handle);
|
||||
buffer_handle_t handleToStore = nullptr;
|
||||
if (handleToImport == nullptr) {
|
||||
LOG(WARNING) << "Failed to duplicate a memory handle. Ignoring a buffer " << b.bufferId;
|
||||
continue;
|
||||
}
|
||||
|
||||
::android::status_t result =
|
||||
mapper.importBuffer(handleToImport, pDesc->width, pDesc->height, pDesc->layers,
|
||||
pDesc->format, pDesc->usage, pDesc->stride, &handleToStore);
|
||||
if (result != ::android::NO_ERROR || handleToStore == nullptr) {
|
||||
LOG(WARNING) << "Failed to import a buffer " << b.bufferId;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool stored = false;
|
||||
for (auto&& rec : mBuffers) {
|
||||
if (rec.handle != nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use this existing entry.
|
||||
rec.handle = handleToStore;
|
||||
rec.inUse = false;
|
||||
stored = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!stored) {
|
||||
// Add a BufferRecord wrapping this handle to our set of available buffers.
|
||||
mBuffers.push_back(BufferRecord(handleToStore));
|
||||
}
|
||||
++mFramesAllowed;
|
||||
}
|
||||
|
||||
*_aidl_return = mFramesAllowed - before;
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
bool EvsMockCamera::setAvailableFrames_Locked(unsigned bufferCount) {
|
||||
if (bufferCount < 1) {
|
||||
LOG(ERROR) << "Ignoring request to set buffer count to zero";
|
||||
return false;
|
||||
}
|
||||
if (bufferCount > kMaxBuffersInFlight) {
|
||||
LOG(ERROR) << "Rejecting buffer request in excess of internal limit";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is an increase required?
|
||||
if (mFramesAllowed < bufferCount) {
|
||||
// An increase is required
|
||||
auto needed = bufferCount - mFramesAllowed;
|
||||
LOG(INFO) << "Allocating " << needed << " buffers for camera frames";
|
||||
|
||||
auto added = increaseAvailableFrames_Locked(needed);
|
||||
if (added != needed) {
|
||||
// If we didn't add all the frames we needed, then roll back to the previous state
|
||||
LOG(ERROR) << "Rolling back to previous frame queue size";
|
||||
decreaseAvailableFrames_Locked(added);
|
||||
return false;
|
||||
}
|
||||
} else if (mFramesAllowed > bufferCount) {
|
||||
// A decrease is required
|
||||
auto framesToRelease = mFramesAllowed - bufferCount;
|
||||
LOG(INFO) << "Returning " << framesToRelease << " camera frame buffers";
|
||||
|
||||
auto released = decreaseAvailableFrames_Locked(framesToRelease);
|
||||
if (released != framesToRelease) {
|
||||
// This shouldn't happen with a properly behaving client because the client
|
||||
// should only make this call after returning sufficient outstanding buffers
|
||||
// to allow a clean resize.
|
||||
LOG(ERROR) << "Buffer queue shrink failed -- too many buffers currently in use?";
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned EvsMockCamera::increaseAvailableFrames_Locked(unsigned numToAdd) {
|
||||
// Acquire the graphics buffer allocator
|
||||
::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
|
||||
|
||||
unsigned added = 0;
|
||||
while (added < numToAdd) {
|
||||
unsigned pixelsPerLine = 0;
|
||||
buffer_handle_t memHandle = nullptr;
|
||||
auto result = alloc.allocate(mWidth, mHeight, mFormat, 1, mUsage, &memHandle,
|
||||
&pixelsPerLine, 0, "EvsMockCamera");
|
||||
if (result != ::android::NO_ERROR) {
|
||||
LOG(ERROR) << "Error " << result << " allocating " << mWidth << " x " << mHeight
|
||||
<< " graphics buffer";
|
||||
break;
|
||||
}
|
||||
if (memHandle == nullptr) {
|
||||
LOG(ERROR) << "We didn't get a buffer handle back from the allocator";
|
||||
break;
|
||||
}
|
||||
if (mStride > 0) {
|
||||
if (mStride != pixelsPerLine) {
|
||||
LOG(ERROR) << "We did not expect to get buffers with different strides!";
|
||||
}
|
||||
} else {
|
||||
// Gralloc defines stride in terms of pixels per line
|
||||
mStride = pixelsPerLine;
|
||||
}
|
||||
|
||||
// Find a place to store the new buffer
|
||||
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.push_back(BufferRecord(memHandle));
|
||||
}
|
||||
|
||||
++mFramesAllowed;
|
||||
++added;
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
unsigned EvsMockCamera::decreaseAvailableFrames_Locked(unsigned numToRemove) {
|
||||
// Acquire the graphics buffer allocator
|
||||
::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
|
||||
|
||||
unsigned removed = 0;
|
||||
for (auto&& rec : mBuffers) {
|
||||
// Is this record not in use, but holding a buffer that we can free?
|
||||
if ((rec.inUse == false) && (rec.handle != nullptr)) {
|
||||
// Release buffer and update the record so we can recognize it as "empty"
|
||||
alloc.free(rec.handle);
|
||||
rec.handle = nullptr;
|
||||
|
||||
--mFramesAllowed;
|
||||
++removed;
|
||||
|
||||
if (removed == numToRemove) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 EvsMockCamera::generateFrames() {
|
||||
LOG(DEBUG) << "Frame generation loop started.";
|
||||
|
||||
unsigned idx = 0;
|
||||
while (true) {
|
||||
bool timeForFrame = false;
|
||||
const nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
|
||||
// Lock scope for updating shared state
|
||||
{
|
||||
std::lock_guard lock(mAccessLock);
|
||||
|
||||
if (mStreamState != RUNNING) {
|
||||
// Break out of our main thread loop
|
||||
break;
|
||||
}
|
||||
|
||||
// Are we allowed to issue another buffer?
|
||||
if (mFramesInUse >= mFramesAllowed) {
|
||||
// Can't do anything right now -- skip this frame
|
||||
LOG(WARNING) << "Skipped a frame because too many are in flight.";
|
||||
} else {
|
||||
// Identify an available buffer to fill
|
||||
for (idx = 0; idx < mBuffers.size(); idx++) {
|
||||
if (!mBuffers[idx].inUse) {
|
||||
if (mBuffers[idx].handle != nullptr) {
|
||||
// Found an available record, so stop looking
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (idx >= mBuffers.size()) {
|
||||
// This shouldn't happen since we already checked mFramesInUse vs mFramesAllowed
|
||||
ALOGE("Failed to find an available buffer slot\n");
|
||||
} else {
|
||||
// We're going to make the frame busy
|
||||
mBuffers[idx].inUse = true;
|
||||
mFramesInUse++;
|
||||
timeForFrame = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (timeForFrame) {
|
||||
using AidlPixelFormat = ::aidl::android::hardware::graphics::common::PixelFormat;
|
||||
|
||||
// Assemble the buffer description we'll transmit below
|
||||
buffer_handle_t memHandle = mBuffers[idx].handle;
|
||||
BufferDesc newBuffer = {
|
||||
.buffer =
|
||||
{
|
||||
.description =
|
||||
{
|
||||
.width = static_cast<int32_t>(mWidth),
|
||||
.height = static_cast<int32_t>(mHeight),
|
||||
.layers = 1,
|
||||
.format = static_cast<AidlPixelFormat>(mFormat),
|
||||
.usage = static_cast<BufferUsage>(mUsage),
|
||||
.stride = static_cast<int32_t>(mStride),
|
||||
},
|
||||
.handle = ::android::dupToAidl(memHandle),
|
||||
},
|
||||
.bufferId = static_cast<int32_t>(idx),
|
||||
.deviceId = mDescription.id,
|
||||
.timestamp = static_cast<int64_t>(::android::elapsedRealtimeNano() *
|
||||
1e+3), // timestamps is in microseconds
|
||||
};
|
||||
|
||||
// Write test data into the image buffer
|
||||
fillMockFrame(memHandle, reinterpret_cast<const AHardwareBuffer_Desc*>(
|
||||
&newBuffer.buffer.description));
|
||||
|
||||
// Issue the (asynchronous) callback to the client -- can't be holding the lock
|
||||
auto flag = false;
|
||||
if (mStream) {
|
||||
std::vector<BufferDesc> frames;
|
||||
frames.push_back(std::move(newBuffer));
|
||||
flag = mStream->deliverFrame(frames).isOk();
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
LOG(DEBUG) << "Delivered " << memHandle << ", id = " << mBuffers[idx].handle;
|
||||
} else {
|
||||
// This can happen if the client dies and is likely unrecoverable.
|
||||
// To avoid consuming resources generating failing calls, we stop sending
|
||||
// frames. Note, however, that the stream remains in the "STREAMING" state
|
||||
// until cleaned up on the main thread.
|
||||
LOG(ERROR) << "Frame delivery call failed in the transport layer.";
|
||||
|
||||
// Since we didn't actually deliver it, mark the frame as available
|
||||
std::lock_guard<std::mutex> lock(mAccessLock);
|
||||
mBuffers[idx].inUse = false;
|
||||
mFramesInUse--;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 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 = {
|
||||
.aType = EvsEventType::STREAM_STOPPED,
|
||||
};
|
||||
if (!mStream->notify(event).isOk()) {
|
||||
ALOGE("Error delivering end of stream marker");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void EvsMockCamera::fillMockFrame(buffer_handle_t handle, const AHardwareBuffer_Desc* pDesc) {
|
||||
// Lock our output buffer for writing
|
||||
uint32_t* pixels = nullptr;
|
||||
::android::GraphicBufferMapper& mapper = ::android::GraphicBufferMapper::get();
|
||||
mapper.lock(handle, GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_NEVER,
|
||||
::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; the colorbar in ABGR format
|
||||
for (unsigned row = 0; row < pDesc->height; row++) {
|
||||
for (unsigned col = 0; col < pDesc->width; col++) {
|
||||
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
|
||||
pixels = pixels + pDesc->stride;
|
||||
}
|
||||
|
||||
// Release our output buffer
|
||||
mapper.unlock(handle);
|
||||
}
|
||||
|
||||
void EvsMockCamera::returnBufferLocked(const uint32_t bufferId) {
|
||||
if (bufferId >= mBuffers.size()) {
|
||||
ALOGE("ignoring doneWithFrame called with invalid bufferId %d (max is %zu)", bufferId,
|
||||
mBuffers.size() - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mBuffers[bufferId].inUse) {
|
||||
ALOGE("ignoring doneWithFrame called on frame %d which is already free", bufferId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark the frame as available
|
||||
mBuffers[bufferId].inUse = false;
|
||||
mFramesInUse--;
|
||||
|
||||
// If this frame's index is high in the array, try to move it down
|
||||
// to improve locality after mFramesAllowed has been reduced.
|
||||
if (bufferId >= mFramesAllowed) {
|
||||
// Find an empty slot lower in the array (which should always exist in this case)
|
||||
for (auto&& rec : mBuffers) {
|
||||
if (rec.handle == nullptr) {
|
||||
rec.handle = mBuffers[bufferId].handle;
|
||||
mBuffers[bufferId].handle = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<EvsMockCamera> EvsMockCamera::Create(const char* deviceName) {
|
||||
std::unique_ptr<ConfigManager::CameraInfo> nullCamInfo = nullptr;
|
||||
|
||||
return Create(deviceName, nullCamInfo);
|
||||
}
|
||||
|
||||
std::shared_ptr<EvsMockCamera> EvsMockCamera::Create(
|
||||
const char* deviceName, std::unique_ptr<ConfigManager::CameraInfo>& camInfo,
|
||||
[[maybe_unused]] const Stream* streamCfg) {
|
||||
std::shared_ptr<EvsMockCamera> c =
|
||||
ndk::SharedRefBase::make<EvsMockCamera>(Sigil{}, deviceName, camInfo);
|
||||
if (!c) {
|
||||
LOG(ERROR) << "Failed to instantiate EvsMockCamera.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 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();
|
||||
c->mWidth = it->second.width;
|
||||
c->mHeight = it->second.height;
|
||||
c->mDescription.vendorFlags = 0xFFFFFFFF; // Arbitrary test value
|
||||
|
||||
c->mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
|
||||
c->mUsage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_CAMERA_WRITE |
|
||||
GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_RARELY;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::automotive::evs::implementation
|
465
automotive/evs/aidl/impl/default/src/GlWrapper.cpp
Normal file
465
automotive/evs/aidl/impl/default/src/GlWrapper.cpp
Normal file
|
@ -0,0 +1,465 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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 <aidl/android/frameworks/automotive/display/DisplayDesc.h>
|
||||
#include <aidl/android/hardware/graphics/common/HardwareBufferDescription.h>
|
||||
#include <aidlcommonsupport/NativeHandle.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>
|
||||
|
||||
namespace {
|
||||
|
||||
using ::aidl::android::frameworks::automotive::display::DisplayDesc;
|
||||
using ::aidl::android::frameworks::automotive::display::ICarDisplayProxy;
|
||||
using ::aidl::android::frameworks::automotive::display::Rotation;
|
||||
using ::aidl::android::hardware::common::NativeHandle;
|
||||
using ::aidl::android::hardware::graphics::common::HardwareBufferDescription;
|
||||
using ::android::GraphicBuffer;
|
||||
using ::android::sp;
|
||||
|
||||
constexpr const char vertexShaderSource[] =
|
||||
"attribute vec4 pos; \n"
|
||||
"attribute vec2 tex; \n"
|
||||
"varying vec2 uv; \n"
|
||||
"void main() \n"
|
||||
"{ \n"
|
||||
" gl_Position = pos; \n"
|
||||
" uv = tex; \n"
|
||||
"} \n";
|
||||
|
||||
constexpr const char pixelShaderSource[] =
|
||||
"precision mediump float; \n"
|
||||
"uniform sampler2D tex; \n"
|
||||
"varying vec2 uv; \n"
|
||||
"void main() \n"
|
||||
"{ \n"
|
||||
" gl_FragColor = texture2D(tex, uv);\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) {
|
||||
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 = (char*)malloc(size);
|
||||
glGetShaderInfoLog(shader, size, nullptr, infoLog);
|
||||
LOG(ERROR) << " msg:" << std::endl << infoLog;
|
||||
free(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);
|
||||
|
||||
glBindAttribLocation(program, 0, "pos");
|
||||
glBindAttribLocation(program, 1, "tex");
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
::android::sp<HGraphicBufferProducer> convertNativeHandleToHGBP(const NativeHandle& aidlHandle) {
|
||||
native_handle_t* handle = ::android::dupFromAidl(aidlHandle);
|
||||
if (handle->numFds != 0 || handle->numInts < std::ceil(sizeof(size_t) / sizeof(int))) {
|
||||
LOG(ERROR) << "Invalid native handle";
|
||||
return nullptr;
|
||||
}
|
||||
::android::hardware::hidl_vec<uint8_t> halToken;
|
||||
halToken.setToExternal(reinterpret_cast<uint8_t*>(const_cast<int*>(&(handle->data[1]))),
|
||||
handle->data[0]);
|
||||
::android::sp<HGraphicBufferProducer> hgbp =
|
||||
HGraphicBufferProducer::castFrom(::android::retrieveHalInterface(halToken));
|
||||
return std::move(hgbp);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace aidl::android::hardware::automotive::evs::implementation {
|
||||
|
||||
// Main entry point
|
||||
bool GlWrapper::initialize(const std::shared_ptr<ICarDisplayProxy>& pWindowProxy,
|
||||
uint64_t displayId) {
|
||||
LOG(DEBUG) << __FUNCTION__;
|
||||
|
||||
if (!pWindowProxy) {
|
||||
LOG(ERROR) << "Could not get ICarDisplayProxy.";
|
||||
return false;
|
||||
}
|
||||
|
||||
DisplayDesc displayDesc;
|
||||
auto status = pWindowProxy->getDisplayInfo(displayId, &displayDesc);
|
||||
if (!status.isOk()) {
|
||||
LOG(ERROR) << "Failed to read the display information";
|
||||
return false;
|
||||
}
|
||||
|
||||
mWidth = displayDesc.width;
|
||||
mHeight = displayDesc.height;
|
||||
if ((displayDesc.orientation != Rotation::ROTATION_0) &&
|
||||
(displayDesc.orientation != Rotation::ROTATION_180)) {
|
||||
std::swap(mWidth, mHeight);
|
||||
}
|
||||
LOG(INFO) << "Display resolution is " << mWidth << "x" << mHeight;
|
||||
|
||||
NativeHandle aidlHandle;
|
||||
status = pWindowProxy->getHGraphicBufferProducer(displayId, &aidlHandle);
|
||||
if (!status.isOk()) {
|
||||
LOG(ERROR) << "Failed to get IGraphicBufferProducer from ICarDisplayProxy.";
|
||||
return false;
|
||||
}
|
||||
|
||||
mGfxBufferProducer = convertNativeHandleToHGBP(aidlHandle);
|
||||
if (!mGfxBufferProducer) {
|
||||
LOG(ERROR) << "Failed to convert a NativeHandle to HGBP.";
|
||||
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 = 2;
|
||||
EGLint minor = 0;
|
||||
if (!eglInitialize(mDisplay, &major, &minor)) {
|
||||
LOG(ERROR) << "Failed to initialize EGL: " << getEGLError();
|
||||
return false;
|
||||
}
|
||||
|
||||
const EGLint config_attribs[] = {
|
||||
// clang-format off
|
||||
// Tag Value
|
||||
EGL_RED_SIZE, 8,
|
||||
EGL_GREEN_SIZE, 8,
|
||||
EGL_BLUE_SIZE, 8,
|
||||
EGL_DEPTH_SIZE, 0,
|
||||
EGL_NONE
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
// 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, " << getEGLError();
|
||||
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
|
||||
if (eglGetCurrentContext() == mContext) {
|
||||
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(const std::shared_ptr<ICarDisplayProxy>& pWindowProxy, uint64_t id) {
|
||||
if (pWindowProxy) {
|
||||
pWindowProxy->showWindow(id);
|
||||
} else {
|
||||
LOG(ERROR) << "ICarDisplayProxy is not available.";
|
||||
}
|
||||
}
|
||||
|
||||
void GlWrapper::hideWindow(const std::shared_ptr<ICarDisplayProxy>& pWindowProxy, uint64_t id) {
|
||||
if (pWindowProxy) {
|
||||
pWindowProxy->hideWindow(id);
|
||||
} else {
|
||||
LOG(ERROR) << "ICarDisplayProxy is not available.";
|
||||
}
|
||||
}
|
||||
|
||||
bool GlWrapper::updateImageTexture(buffer_handle_t handle,
|
||||
const HardwareBufferDescription& description) {
|
||||
if (mKHRimage != EGL_NO_IMAGE_KHR) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create a temporary GraphicBuffer to wrap the provided handle.
|
||||
sp<GraphicBuffer> pGfxBuffer =
|
||||
new GraphicBuffer(description.width, description.height,
|
||||
static_cast<::android::PixelFormat>(description.format),
|
||||
description.layers, static_cast<uint32_t>(description.usage),
|
||||
description.stride, const_cast<native_handle_t*>(handle),
|
||||
/* keepOwnership= */ false);
|
||||
if (!pGfxBuffer) {
|
||||
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(0.1f, 0.5f, 0.1f, 1.0f);
|
||||
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[] = {
|
||||
// clang-format off
|
||||
-0.8, 0.8, 0.0f, // left top in window space
|
||||
0.8, 0.8, 0.0f, // right top
|
||||
-0.8, -0.8, 0.0f, // left bottom
|
||||
0.8, -0.8, 0.0f // right bottom
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
// NOTE: We didn't flip the image in the texture, so V=0 is actually the top of the image
|
||||
GLfloat vertsCarTex[] = {
|
||||
// clang-format off
|
||||
0.0f, 0.0f, // left top
|
||||
1.0f, 0.0f, // right top
|
||||
0.0f, 1.0f, // left bottom
|
||||
1.0f, 1.0f // right bottom
|
||||
// clang-format on
|
||||
};
|
||||
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();
|
||||
|
||||
if (eglSwapBuffers(mDisplay, mSurface) == EGL_FALSE) {
|
||||
LOG(WARNING) << "Failed to swap EGL buffers, " << getEGLError();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::automotive::evs::implementation
|
|
@ -14,38 +14,75 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "EvsService"
|
||||
|
||||
#include <DefaultEvsEnumerator.h>
|
||||
#include "EvsEnumerator.h"
|
||||
#include "EvsGlDisplay.h"
|
||||
|
||||
#include <android/binder_manager.h>
|
||||
#include <android/binder_process.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
using ::aidl::android::hardware::automotive::evs::implementation::DefaultEvsEnumerator;
|
||||
#include <unistd.h>
|
||||
|
||||
int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {
|
||||
std::shared_ptr<DefaultEvsEnumerator> vhal = ndk::SharedRefBase::make<DefaultEvsEnumerator>();
|
||||
#include <atomic>
|
||||
#include <cstdlib>
|
||||
#include <string_view>
|
||||
|
||||
ALOGI("Registering as service...");
|
||||
binder_exception_t err =
|
||||
AServiceManager_addService(vhal->asBinder().get(), "android.hardware.automotive.evs");
|
||||
namespace {
|
||||
|
||||
using ::aidl::android::frameworks::automotive::display::ICarDisplayProxy;
|
||||
using ::aidl::android::hardware::automotive::evs::implementation::EvsEnumerator;
|
||||
|
||||
constexpr std::string_view kDisplayServiceInstanceName = "/default";
|
||||
constexpr std::string_view kHwInstanceName = "/hw/0";
|
||||
constexpr int kNumBinderThreads = 1;
|
||||
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
LOG(INFO) << "EVS Hardware Enumerator service is starting";
|
||||
|
||||
const std::string displayServiceInstanceName =
|
||||
std::string(ICarDisplayProxy::descriptor) + std::string(kDisplayServiceInstanceName);
|
||||
if (!AServiceManager_isDeclared(displayServiceInstanceName.data())) {
|
||||
// TODO: We may just want to disable EVS display.
|
||||
LOG(ERROR) << displayServiceInstanceName << " is required.";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::shared_ptr<ICarDisplayProxy> displayService = ICarDisplayProxy::fromBinder(
|
||||
::ndk::SpAIBinder(AServiceManager_waitForService(displayServiceInstanceName.data())));
|
||||
if (!displayService) {
|
||||
LOG(ERROR) << "Cannot use " << displayServiceInstanceName << ". Exiting.";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Register our service -- if somebody is already registered by our name,
|
||||
// they will be killed (their thread pool will throw an exception).
|
||||
std::shared_ptr<EvsEnumerator> service =
|
||||
ndk::SharedRefBase::make<EvsEnumerator>(displayService);
|
||||
if (!service) {
|
||||
LOG(ERROR) << "Failed to instantiate the service";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const std::string instanceName =
|
||||
std::string(EvsEnumerator::descriptor) + std::string(kHwInstanceName);
|
||||
auto err = AServiceManager_addService(service->asBinder().get(), instanceName.data());
|
||||
if (err != EX_NONE) {
|
||||
ALOGE("failed to register android.hardware.automotive.evs service, exception: %d", err);
|
||||
return 1;
|
||||
LOG(ERROR) << "Failed to register " << instanceName << ", exception = " << err;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!ABinderProcess_setThreadPoolMaxThreadCount(1)) {
|
||||
ALOGE("%s", "failed to set thread pool max thread count");
|
||||
return 1;
|
||||
if (!ABinderProcess_setThreadPoolMaxThreadCount(kNumBinderThreads)) {
|
||||
LOG(ERROR) << "Failed to set thread pool";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ABinderProcess_startThreadPool();
|
||||
|
||||
ALOGI("Evs Service Ready");
|
||||
LOG(INFO) << "EVS Hardware Enumerator is ready";
|
||||
|
||||
ABinderProcess_joinThreadPool();
|
||||
|
||||
ALOGI("Evs Service Exiting");
|
||||
|
||||
return 0;
|
||||
// In normal operation, we don't expect the thread pool to exit
|
||||
LOG(INFO) << "EVS Hardware Enumerator is shutting down";
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -84,15 +84,6 @@
|
|||
<regex-instance>[a-z]+/[0-9]+</regex-instance>
|
||||
</interface>
|
||||
</hal>
|
||||
<hal format="hidl" optional="true">
|
||||
<name>android.hardware.automotive.evs</name>
|
||||
<version>1.0-1</version>
|
||||
<interface>
|
||||
<name>IEvsEnumerator</name>
|
||||
<instance>default</instance>
|
||||
<regex-instance>[a-z]+/[0-9]+</regex-instance>
|
||||
</interface>
|
||||
</hal>
|
||||
<hal format="aidl" optional="true">
|
||||
<name>android.hardware.automotive.occupant_awareness</name>
|
||||
<version>1</version>
|
||||
|
|
Loading…
Reference in a new issue