mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-02 20:24:19 +00:00
MultiHal 2.0 - setOperationMode and init direct channel flags
Implement setOperationMode method of HalProxy object and initialized the
direct channel flag for the sensors list. Also create some unit tests to
test both of these new additions in HalProxy_test.cpp.
Bug: 136511617
Test: Tested using unit tests and tested on device with both fake
subhals
Change-Id: I4e39ca0e94b3e109706d628612d1db9c98aca053
This commit is contained in:
@@ -45,6 +45,7 @@ cc_binary {
|
||||
],
|
||||
init_rc: ["android.hardware.sensors@2.0-service-multihal.rc"],
|
||||
vintf_fragments: ["android.hardware.sensors@2.0-multihal.xml"],
|
||||
cflags: ["-DLOG_TAG=\"SensorsMultiHal\""],
|
||||
}
|
||||
|
||||
cc_library_headers {
|
||||
|
||||
@@ -69,8 +69,8 @@ class SensorsCallbackProxy : public ISensorsCallback {
|
||||
};
|
||||
|
||||
HalProxy::HalProxy() {
|
||||
const char* kMultiHalConfigFilePath = "/vendor/etc/sensors/hals.conf";
|
||||
initializeSubHalListFromConfigFile(kMultiHalConfigFilePath);
|
||||
const char* kMultiHalConfigFile = "/vendor/etc/sensors/hals.conf";
|
||||
initializeSubHalListFromConfigFile(kMultiHalConfigFile);
|
||||
initializeSensorList();
|
||||
}
|
||||
|
||||
@@ -88,9 +88,27 @@ Return<void> HalProxy::getSensorsList(getSensorsList_cb _hidl_cb) {
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<Result> HalProxy::setOperationMode(OperationMode /* mode */) {
|
||||
// TODO: Proxy API call to all sub-HALs and return appropriate result.
|
||||
return Result::INVALID_OPERATION;
|
||||
Return<Result> HalProxy::setOperationMode(OperationMode mode) {
|
||||
Result result = Result::OK;
|
||||
size_t subHalIndex;
|
||||
for (subHalIndex = 0; subHalIndex < mSubHalList.size(); subHalIndex++) {
|
||||
ISensorsSubHal* subHal = mSubHalList[subHalIndex];
|
||||
result = subHal->setOperationMode(mode);
|
||||
if (result != Result::OK) {
|
||||
ALOGE("setOperationMode failed for SubHal: %s", subHal->getName().c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result != Result::OK) {
|
||||
// Reset the subhal operation modes that have been flipped
|
||||
for (size_t i = 0; i < subHalIndex; i++) {
|
||||
ISensorsSubHal* subHal = mSubHalList[i];
|
||||
subHal->setOperationMode(mCurrentOperationMode);
|
||||
}
|
||||
} else {
|
||||
mCurrentOperationMode = mode;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Return<Result> HalProxy::activate(int32_t sensorHandle, bool enabled) {
|
||||
@@ -190,27 +208,27 @@ Return<void> HalProxy::onDynamicSensorsDisconnected(
|
||||
void HalProxy::initializeSubHalListFromConfigFile(const char* configFileName) {
|
||||
std::ifstream subHalConfigStream(configFileName);
|
||||
if (!subHalConfigStream) {
|
||||
LOG_FATAL("Failed to load subHal config file: %s", configFileName);
|
||||
ALOGE("Failed to load subHal config file: %s", configFileName);
|
||||
} else {
|
||||
std::string subHalLibraryFile;
|
||||
while (subHalConfigStream >> subHalLibraryFile) {
|
||||
void* handle = dlopen(subHalLibraryFile.c_str(), RTLD_NOW);
|
||||
if (handle == nullptr) {
|
||||
LOG_FATAL("dlopen failed for library: %s", subHalLibraryFile.c_str());
|
||||
ALOGE("dlopen failed for library: %s", subHalLibraryFile.c_str());
|
||||
} else {
|
||||
SensorsHalGetSubHalFunc* sensorsHalGetSubHalPtr =
|
||||
(SensorsHalGetSubHalFunc*)dlsym(handle, "sensorsHalGetSubHal");
|
||||
if (sensorsHalGetSubHalPtr == nullptr) {
|
||||
LOG_FATAL("Failed to locate sensorsHalGetSubHal function for library: %s",
|
||||
subHalLibraryFile.c_str());
|
||||
ALOGE("Failed to locate sensorsHalGetSubHal function for library: %s",
|
||||
subHalLibraryFile.c_str());
|
||||
} else {
|
||||
std::function<SensorsHalGetSubHalFunc> sensorsHalGetSubHal =
|
||||
*sensorsHalGetSubHalPtr;
|
||||
uint32_t version;
|
||||
ISensorsSubHal* subHal = sensorsHalGetSubHal(&version);
|
||||
if (version != SUB_HAL_2_0_VERSION) {
|
||||
LOG_FATAL("SubHal version was not 2.0 for library: %s",
|
||||
subHalLibraryFile.c_str());
|
||||
ALOGE("SubHal version was not 2.0 for library: %s",
|
||||
subHalLibraryFile.c_str());
|
||||
} else {
|
||||
ALOGV("Loaded SubHal from library: %s", subHalLibraryFile.c_str());
|
||||
mSubHalList.push_back(subHal);
|
||||
@@ -231,6 +249,7 @@ void HalProxy::initializeSensorList() {
|
||||
} else {
|
||||
ALOGV("Loaded sensor: %s", sensor.name.c_str());
|
||||
sensor.sensorHandle |= (subHalIndex << 24);
|
||||
setDirectChannelFlags(&sensor, subHal);
|
||||
mSensorList.push_back(sensor);
|
||||
}
|
||||
}
|
||||
@@ -241,6 +260,20 @@ void HalProxy::initializeSensorList() {
|
||||
}
|
||||
}
|
||||
|
||||
void HalProxy::setDirectChannelFlags(SensorInfo* sensorInfo, ISensorsSubHal* subHal) {
|
||||
bool sensorSupportsDirectChannel =
|
||||
(sensorInfo->flags & (V1_0::SensorFlagBits::MASK_DIRECT_REPORT |
|
||||
V1_0::SensorFlagBits::MASK_DIRECT_CHANNEL)) != 0;
|
||||
if (mDirectChannelSubHal == nullptr && sensorSupportsDirectChannel) {
|
||||
mDirectChannelSubHal = subHal;
|
||||
} else if (mDirectChannelSubHal != nullptr && subHal != mDirectChannelSubHal) {
|
||||
// disable direct channel capability for sensors in subHals that are not
|
||||
// the only one we will enable
|
||||
sensorInfo->flags &= ~(V1_0::SensorFlagBits::MASK_DIRECT_REPORT |
|
||||
V1_0::SensorFlagBits::MASK_DIRECT_CHANNEL);
|
||||
}
|
||||
}
|
||||
|
||||
ISensorsSubHal* HalProxy::getSubHalForSensorHandle(uint32_t sensorHandle) {
|
||||
return mSubHalList[static_cast<size_t>(sensorHandle >> 24)];
|
||||
}
|
||||
|
||||
@@ -125,10 +125,16 @@ class HalProxy : public ISensors {
|
||||
* well as the modified sensor handle for the framework.
|
||||
*
|
||||
* The subhal index is encoded in the first byte of the sensor handle and
|
||||
* the remaining bytes are generated by the subhal.
|
||||
* the remaining bytes are generated by the subhal to identify the sensor.
|
||||
*/
|
||||
std::vector<SensorInfo> mSensorList;
|
||||
|
||||
//! The current operation mode for all subhals.
|
||||
OperationMode mCurrentOperationMode = OperationMode::NORMAL;
|
||||
|
||||
//! The single subHal that supports directChannel reporting.
|
||||
ISensorsSubHal* mDirectChannelSubHal = nullptr;
|
||||
|
||||
/**
|
||||
* Initialize the list of SubHal objects in mSubHalList by reading from dynamic libraries
|
||||
* listed in a config file.
|
||||
@@ -141,6 +147,17 @@ class HalProxy : public ISensors {
|
||||
*/
|
||||
void initializeSensorList();
|
||||
|
||||
/**
|
||||
* Clear direct channel flags if the HalProxy has already chosen a subhal as its direct channel
|
||||
* subhal. Set the directChannelSubHal pointer to the subHal passed in if this is the first
|
||||
* direct channel enabled sensor seen.
|
||||
*
|
||||
* @param sensorInfo The SensorInfo object that may be altered to have direct channel support
|
||||
* disabled.
|
||||
* @param subHal The subhal pointer that the current sensorInfo object came from.
|
||||
*/
|
||||
void setDirectChannelFlags(SensorInfo* sensorInfo, ISensorsSubHal* subHal);
|
||||
|
||||
/*
|
||||
* Get the subhal pointer which can be found by indexing into the mSubHalList vector
|
||||
* using the index from the first byte of sensorHandle.
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "android.hardware.sensors@2.0-service"
|
||||
|
||||
#include <android/hardware/sensors/2.0/ISensors.h>
|
||||
#include <hidl/HidlTransportSupport.h>
|
||||
#include <log/log.h>
|
||||
|
||||
@@ -15,33 +15,56 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <android/hardware/sensors/2.0/types.h>
|
||||
|
||||
#include "HalProxy.h"
|
||||
#include "SensorsSubHal.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
using ::android::hardware::sensors::V1_0::SensorFlagBits;
|
||||
using ::android::hardware::sensors::V1_0::SensorInfo;
|
||||
using ::android::hardware::sensors::V1_0::SensorType;
|
||||
using ::android::hardware::sensors::V2_0::implementation::HalProxy;
|
||||
using ::android::hardware::sensors::V2_0::subhal::implementation::AllSensorsSubHal;
|
||||
using ::android::hardware::sensors::V2_0::subhal::implementation::
|
||||
AllSupportDirectChannelSensorsSubHal;
|
||||
using ::android::hardware::sensors::V2_0::subhal::implementation::ContinuousSensorsSubHal;
|
||||
using ::android::hardware::sensors::V2_0::subhal::implementation::
|
||||
DoesNotSupportDirectChannelSensorsSubHal;
|
||||
using ::android::hardware::sensors::V2_0::subhal::implementation::OnChangeSensorsSubHal;
|
||||
using ::android::hardware::sensors::V2_0::subhal::implementation::SensorsSubHal;
|
||||
|
||||
// TODO: Add more interesting tests such as
|
||||
// - verify setOperationMode invokes all subhals
|
||||
// - verify if a subhal fails to change operation mode, that state is reset properly
|
||||
// - Available sensors are obtained during initialization
|
||||
//
|
||||
// You can run this suite using "atest android.hardware.sensors@2.0-halproxy-unit-tests".
|
||||
//
|
||||
// See https://source.android.com/compatibility/tests/development/native-func-e2e.md for more info
|
||||
// on how tests are set up and for information on the gtest framework itself.
|
||||
using ::android::hardware::sensors::V2_0::subhal::implementation::
|
||||
SetOperationModeFailingSensorsSubHal;
|
||||
|
||||
// Helper declarations follow
|
||||
|
||||
/**
|
||||
* Tests that for each SensorInfo object from a proxy getSensorsList call each corresponding
|
||||
* object from a subhal getSensorsList call has the same type and its last 3 bytes are the
|
||||
* same for sensorHandle field.
|
||||
*
|
||||
* @param proxySensorsList The list of SensorInfo objects from the proxy.getSensorsList callback.
|
||||
* @param subHalSenosrsList The list of SensorInfo objects from the subHal.getSensorsList callback.
|
||||
*/
|
||||
void testSensorsListFromProxyAndSubHal(const std::vector<SensorInfo>& proxySensorsList,
|
||||
const std::vector<SensorInfo>& subHalSensorsList);
|
||||
|
||||
/**
|
||||
* Tests that there is exactly one subhal that allows its sensors to have direct channel enabled.
|
||||
* Therefore, all SensorInfo objects that are not from the enabled subhal should be disabled for
|
||||
* direct channel.
|
||||
*
|
||||
* @param sensorsList The SensorInfo object list from proxy.getSensorsList call.
|
||||
* @param enabledSubHalIndex The index of the subhal in the halproxy that is expected to be
|
||||
* enabled.
|
||||
*/
|
||||
void testSensorsListForOneDirectChannelEnabledSubHal(const std::vector<SensorInfo>& sensorsList,
|
||||
size_t enabledSubHalIndex);
|
||||
|
||||
// Tests follow
|
||||
TEST(HalProxyTest, GetSensorsListOneSubHalTest) {
|
||||
AllSensorsSubHal subHal;
|
||||
@@ -76,6 +99,63 @@ TEST(HalProxyTest, GetSensorsListTwoSubHalTest) {
|
||||
testSensorsListFromProxyAndSubHal(proxySensorsList, combinedSubHalSensorsList);
|
||||
}
|
||||
|
||||
TEST(HalProxyTest, SetOperationModeTwoSubHalSuccessTest) {
|
||||
ContinuousSensorsSubHal subHal1;
|
||||
OnChangeSensorsSubHal subHal2;
|
||||
|
||||
std::vector<ISensorsSubHal*> fakeSubHals{&subHal1, &subHal2};
|
||||
HalProxy proxy(fakeSubHals);
|
||||
|
||||
EXPECT_EQ(subHal1.getOperationMode(), OperationMode::NORMAL);
|
||||
EXPECT_EQ(subHal2.getOperationMode(), OperationMode::NORMAL);
|
||||
|
||||
Result result = proxy.setOperationMode(OperationMode::DATA_INJECTION);
|
||||
|
||||
EXPECT_EQ(result, Result::OK);
|
||||
EXPECT_EQ(subHal1.getOperationMode(), OperationMode::DATA_INJECTION);
|
||||
EXPECT_EQ(subHal2.getOperationMode(), OperationMode::DATA_INJECTION);
|
||||
}
|
||||
|
||||
TEST(HalProxyTest, SetOperationModeTwoSubHalFailTest) {
|
||||
AllSensorsSubHal subHal1;
|
||||
SetOperationModeFailingSensorsSubHal subHal2;
|
||||
|
||||
std::vector<ISensorsSubHal*> fakeSubHals{&subHal1, &subHal2};
|
||||
HalProxy proxy(fakeSubHals);
|
||||
|
||||
EXPECT_EQ(subHal1.getOperationMode(), OperationMode::NORMAL);
|
||||
EXPECT_EQ(subHal2.getOperationMode(), OperationMode::NORMAL);
|
||||
|
||||
Result result = proxy.setOperationMode(OperationMode::DATA_INJECTION);
|
||||
|
||||
EXPECT_NE(result, Result::OK);
|
||||
EXPECT_EQ(subHal1.getOperationMode(), OperationMode::NORMAL);
|
||||
EXPECT_EQ(subHal2.getOperationMode(), OperationMode::NORMAL);
|
||||
}
|
||||
|
||||
TEST(HalProxyTest, InitDirectChannelTwoSubHalsUnitTest) {
|
||||
AllSupportDirectChannelSensorsSubHal subHal1;
|
||||
AllSupportDirectChannelSensorsSubHal subHal2;
|
||||
|
||||
std::vector<ISensorsSubHal*> fakeSubHals{&subHal1, &subHal2};
|
||||
HalProxy proxy(fakeSubHals);
|
||||
|
||||
proxy.getSensorsList([&](const auto& sensorsList) {
|
||||
testSensorsListForOneDirectChannelEnabledSubHal(sensorsList, 0);
|
||||
});
|
||||
}
|
||||
|
||||
TEST(HalProxyTest, InitDirectChannelThreeSubHalsUnitTest) {
|
||||
DoesNotSupportDirectChannelSensorsSubHal subHal1;
|
||||
AllSupportDirectChannelSensorsSubHal subHal2, subHal3;
|
||||
std::vector<ISensorsSubHal*> fakeSubHals{&subHal1, &subHal2, &subHal3};
|
||||
HalProxy proxy(fakeSubHals);
|
||||
|
||||
proxy.getSensorsList([&](const auto& sensorsList) {
|
||||
testSensorsListForOneDirectChannelEnabledSubHal(sensorsList, 1);
|
||||
});
|
||||
}
|
||||
|
||||
// Helper implementations follow
|
||||
void testSensorsListFromProxyAndSubHal(const std::vector<SensorInfo>& proxySensorsList,
|
||||
const std::vector<SensorInfo>& subHalSensorsList) {
|
||||
@@ -88,4 +168,23 @@ void testSensorsListFromProxyAndSubHal(const std::vector<SensorInfo>& proxySenso
|
||||
EXPECT_EQ(proxySensor.type, subHalSensor.type);
|
||||
EXPECT_EQ(proxySensor.sensorHandle & 0x00FFFFFF, subHalSensor.sensorHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void testSensorsListForOneDirectChannelEnabledSubHal(const std::vector<SensorInfo>& sensorsList,
|
||||
size_t enabledSubHalIndex) {
|
||||
for (const SensorInfo& sensor : sensorsList) {
|
||||
size_t subHalIndex = static_cast<size_t>(sensor.sensorHandle >> 24);
|
||||
if (subHalIndex == enabledSubHalIndex) {
|
||||
// First subhal should have been picked as the direct channel subhal
|
||||
// and so have direct channel enabled on all of its sensors
|
||||
EXPECT_NE(sensor.flags & SensorFlagBits::MASK_DIRECT_REPORT, 0);
|
||||
EXPECT_NE(sensor.flags & SensorFlagBits::MASK_DIRECT_CHANNEL, 0);
|
||||
} else {
|
||||
// All other subhals should have direct channel disabled for all sensors
|
||||
EXPECT_EQ(sensor.flags & SensorFlagBits::MASK_DIRECT_REPORT, 0);
|
||||
EXPECT_EQ(sensor.flags & SensorFlagBits::MASK_DIRECT_CHANNEL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -68,6 +68,7 @@ Return<Result> SensorsSubHal::setOperationMode(OperationMode mode) {
|
||||
for (auto sensor : mSensors) {
|
||||
sensor.second->setOperationMode(mode);
|
||||
}
|
||||
mCurrentOperationMode = mode;
|
||||
return Result::OK;
|
||||
}
|
||||
|
||||
@@ -192,6 +193,34 @@ AllSensorsSubHal::AllSensorsSubHal() {
|
||||
AddSensor<RelativeHumiditySensor>();
|
||||
}
|
||||
|
||||
Return<Result> SetOperationModeFailingSensorsSubHal::setOperationMode(OperationMode /*mode*/) {
|
||||
return Result::BAD_VALUE;
|
||||
}
|
||||
|
||||
Return<void> AllSupportDirectChannelSensorsSubHal::getSensorsList(getSensorsList_cb _hidl_cb) {
|
||||
std::vector<SensorInfo> sensors;
|
||||
for (const auto& sensor : mSensors) {
|
||||
SensorInfo sensorInfo = sensor.second->getSensorInfo();
|
||||
sensorInfo.flags |= V1_0::SensorFlagBits::MASK_DIRECT_CHANNEL;
|
||||
sensorInfo.flags |= V1_0::SensorFlagBits::MASK_DIRECT_REPORT;
|
||||
sensors.push_back(sensorInfo);
|
||||
}
|
||||
_hidl_cb(sensors);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> DoesNotSupportDirectChannelSensorsSubHal::getSensorsList(getSensorsList_cb _hidl_cb) {
|
||||
std::vector<SensorInfo> sensors;
|
||||
for (const auto& sensor : mSensors) {
|
||||
SensorInfo sensorInfo = sensor.second->getSensorInfo();
|
||||
sensorInfo.flags &= ~static_cast<uint32_t>(V1_0::SensorFlagBits::MASK_DIRECT_CHANNEL);
|
||||
sensorInfo.flags &= ~static_cast<uint32_t>(V1_0::SensorFlagBits::MASK_DIRECT_REPORT);
|
||||
sensors.push_back(sensorInfo);
|
||||
}
|
||||
_hidl_cb(sensors);
|
||||
return Void();
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace subhal
|
||||
} // namespace V2_0
|
||||
|
||||
@@ -29,6 +29,8 @@ namespace V2_0 {
|
||||
namespace subhal {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::hardware::sensors::V1_0::OperationMode;
|
||||
using ::android::hardware::sensors::V1_0::Result;
|
||||
using ::android::hardware::sensors::V2_0::implementation::IHalProxyCallback;
|
||||
|
||||
/**
|
||||
@@ -37,18 +39,18 @@ using ::android::hardware::sensors::V2_0::implementation::IHalProxyCallback;
|
||||
*/
|
||||
class SensorsSubHal : public ISensorsSubHal, public ISensorsEventCallback {
|
||||
using Event = ::android::hardware::sensors::V1_0::Event;
|
||||
using OperationMode = ::android::hardware::sensors::V1_0::OperationMode;
|
||||
using RateLevel = ::android::hardware::sensors::V1_0::RateLevel;
|
||||
using Result = ::android::hardware::sensors::V1_0::Result;
|
||||
using SharedMemInfo = ::android::hardware::sensors::V1_0::SharedMemInfo;
|
||||
|
||||
public:
|
||||
SensorsSubHal();
|
||||
|
||||
// Methods from ::android::hardware::sensors::V2_0::ISensors follow.
|
||||
Return<void> getSensorsList(getSensorsList_cb _hidl_cb) override;
|
||||
virtual Return<void> getSensorsList(getSensorsList_cb _hidl_cb) override;
|
||||
|
||||
Return<Result> setOperationMode(OperationMode mode) override;
|
||||
virtual Return<Result> setOperationMode(OperationMode mode) override;
|
||||
|
||||
OperationMode getOperationMode() const { return mCurrentOperationMode; }
|
||||
|
||||
Return<Result> activate(int32_t sensorHandle, bool enabled) override;
|
||||
|
||||
@@ -91,7 +93,18 @@ class SensorsSubHal : public ISensorsSubHal, public ISensorsEventCallback {
|
||||
mSensors[sensor->getSensorInfo().sensorHandle] = sensor;
|
||||
}
|
||||
|
||||
/**
|
||||
* A map of the available sensors
|
||||
*/
|
||||
std::map<int32_t, std::shared_ptr<Sensor>> mSensors;
|
||||
|
||||
private:
|
||||
/**
|
||||
* The current operation mode of the multihal framework. Ensures that all subhals are set to
|
||||
* the same operation mode.
|
||||
*/
|
||||
OperationMode mCurrentOperationMode = OperationMode::NORMAL;
|
||||
|
||||
/**
|
||||
* Callback used to communicate to the HalProxy when dynamic sensors are connected /
|
||||
* disconnected, sensor events need to be sent to the framework, and when a wakelock should be
|
||||
@@ -99,11 +112,6 @@ class SensorsSubHal : public ISensorsSubHal, public ISensorsEventCallback {
|
||||
*/
|
||||
sp<IHalProxyCallback> mCallback;
|
||||
|
||||
/**
|
||||
* A map of the available sensors
|
||||
*/
|
||||
std::map<int32_t, std::shared_ptr<Sensor>> mSensors;
|
||||
|
||||
/**
|
||||
* The next available sensor handle
|
||||
*/
|
||||
@@ -128,6 +136,21 @@ class AllSensorsSubHal : public SensorsSubHal {
|
||||
AllSensorsSubHal();
|
||||
};
|
||||
|
||||
class SetOperationModeFailingSensorsSubHal : public AllSensorsSubHal {
|
||||
public:
|
||||
Return<Result> setOperationMode(OperationMode mode) override;
|
||||
};
|
||||
|
||||
class AllSupportDirectChannelSensorsSubHal : public AllSensorsSubHal {
|
||||
public:
|
||||
Return<void> getSensorsList(getSensorsList_cb _hidl_cb) override;
|
||||
};
|
||||
|
||||
class DoesNotSupportDirectChannelSensorsSubHal : public AllSensorsSubHal {
|
||||
public:
|
||||
Return<void> getSensorsList(getSensorsList_cb _hidl_cb) override;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace subhal
|
||||
} // namespace V2_0
|
||||
|
||||
Reference in New Issue
Block a user