Merge "Moved emulated User HAL capabilities into a library." into rvc-dev

This commit is contained in:
TreeHugger Robot 2020-04-27 19:23:12 +00:00 committed by Android (Google) Code Review
commit d2115c9876
7 changed files with 338 additions and 196 deletions

View file

@ -75,7 +75,10 @@ cc_library_static {
],
local_include_dirs: ["common/include/vhal_v2_0"],
export_include_dirs: ["impl"],
whole_static_libs: ["android.hardware.automotive.vehicle@2.0-manager-lib"],
whole_static_libs: [
"android.hardware.automotive.vehicle@2.0-emulated-user-hal-lib",
"android.hardware.automotive.vehicle@2.0-manager-lib",
],
shared_libs: [
"libbase",
"libjsoncpp",
@ -87,6 +90,16 @@ cc_library_static {
],
}
// Library used to emulate User HAL behavior through lshal debug requests.
cc_library_static {
name: "android.hardware.automotive.vehicle@2.0-emulated-user-hal-lib",
vendor: true,
defaults: ["vhal_v2_0_defaults"],
srcs: [
"impl/vhal_v2_0/EmulatedUserHal.cpp",
],
}
cc_test {
name: "android.hardware.automotive.vehicle@2.0-manager-unit-tests",
vendor: true,

View file

@ -79,8 +79,6 @@ constexpr int WHEEL_FRONT_LEFT = (int)VehicleAreaWheel::LEFT_FRONT;
constexpr int WHEEL_FRONT_RIGHT = (int)VehicleAreaWheel::RIGHT_FRONT;
constexpr int WHEEL_REAR_LEFT = (int)VehicleAreaWheel::LEFT_REAR;
constexpr int WHEEL_REAR_RIGHT = (int)VehicleAreaWheel::RIGHT_REAR;
constexpr int INITIAL_USER_INFO = (int)VehicleProperty::INITIAL_USER_INFO;
constexpr int SWITCH_USER = (int)VehicleProperty::SWITCH_USER;
/**
* This property is used for test purpose to generate fake events. Here is the test package that

View file

@ -0,0 +1,186 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "EmulatedUserHal"
#include <cutils/log.h>
#include <utils/SystemClock.h>
#include "EmulatedUserHal.h"
namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {
namespace V2_0 {
namespace impl {
constexpr int INITIAL_USER_INFO = static_cast<int>(VehicleProperty::INITIAL_USER_INFO);
constexpr int SWITCH_USER = static_cast<int>(VehicleProperty::SWITCH_USER);
bool EmulatedUserHal::isSupported(int32_t prop) {
switch (prop) {
case INITIAL_USER_INFO:
case SWITCH_USER:
return true;
default:
return false;
}
}
android::base::Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onSetProperty(
const VehiclePropValue& value) {
ALOGV("onSetProperty(): %s", toString(value).c_str());
switch (value.prop) {
case INITIAL_USER_INFO:
return onSetInitialUserInfoResponse(value);
case SWITCH_USER:
return onSetSwitchUserResponse(value);
default:
return android::base::Error(static_cast<int>(StatusCode::INVALID_ARG))
<< "Unsupported property: " << toString(value);
}
}
android::base::Result<std::unique_ptr<VehiclePropValue>>
EmulatedUserHal::onSetInitialUserInfoResponse(const VehiclePropValue& value) {
if (value.value.int32Values.size() == 0) {
ALOGE("set(INITIAL_USER_INFO): no int32values, ignoring it: %s", toString(value).c_str());
return android::base::Error(static_cast<int>(StatusCode::INVALID_ARG))
<< "no int32values on " << toString(value);
}
if (value.areaId != 0) {
ALOGD("set(INITIAL_USER_INFO) called from lshal; storing it: %s", toString(value).c_str());
mInitialUserResponseFromCmd.reset(new VehiclePropValue(value));
return {};
}
ALOGD("set(INITIAL_USER_INFO) called from Android: %s", toString(value).c_str());
int32_t requestId = value.value.int32Values[0];
if (mInitialUserResponseFromCmd != nullptr) {
ALOGI("replying INITIAL_USER_INFO with lshal value: %s",
toString(*mInitialUserResponseFromCmd).c_str());
return sendUserHalResponse(std::move(mInitialUserResponseFromCmd), requestId);
}
// Returns default response
auto updatedValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue);
updatedValue->prop = INITIAL_USER_INFO;
updatedValue->timestamp = elapsedRealtimeNano();
updatedValue->value.int32Values.resize(2);
updatedValue->value.int32Values[0] = requestId;
updatedValue->value.int32Values[1] = (int32_t)InitialUserInfoResponseAction::DEFAULT;
ALOGI("no lshal response; replying with InitialUserInfoResponseAction::DEFAULT: %s",
toString(*updatedValue).c_str());
return updatedValue;
}
android::base::Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onSetSwitchUserResponse(
const VehiclePropValue& value) {
if (value.value.int32Values.size() == 0) {
ALOGE("set(SWITCH_USER): no int32values, ignoring it: %s", toString(value).c_str());
return android::base::Error(static_cast<int>(StatusCode::INVALID_ARG))
<< "no int32values on " << toString(value);
}
if (value.areaId != 0) {
ALOGD("set(SWITCH_USER) called from lshal; storing it: %s", toString(value).c_str());
mSwitchUserResponseFromCmd.reset(new VehiclePropValue(value));
return {};
}
ALOGD("set(SWITCH_USER) called from Android: %s", toString(value).c_str());
int32_t requestId = value.value.int32Values[0];
if (mSwitchUserResponseFromCmd != nullptr) {
ALOGI("replying SWITCH_USER with lshal value: %s",
toString(*mSwitchUserResponseFromCmd).c_str());
return sendUserHalResponse(std::move(mSwitchUserResponseFromCmd), requestId);
}
// Returns default response
auto updatedValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue);
updatedValue->prop = SWITCH_USER;
updatedValue->timestamp = elapsedRealtimeNano();
updatedValue->value.int32Values.resize(3);
updatedValue->value.int32Values[0] = requestId;
updatedValue->value.int32Values[1] = (int32_t)SwitchUserMessageType::VEHICLE_RESPONSE;
updatedValue->value.int32Values[2] = (int32_t)SwitchUserStatus::SUCCESS;
ALOGI("no lshal response; replying with VEHICLE_RESPONSE / SUCCESS: %s",
toString(*updatedValue).c_str());
return updatedValue;
}
android::base::Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::sendUserHalResponse(
std::unique_ptr<VehiclePropValue> response, int32_t requestId) {
switch (response->areaId) {
case 1:
ALOGD("returning response with right request id");
response->value.int32Values[0] = requestId;
break;
case 2:
ALOGD("returning response with wrong request id");
response->value.int32Values[0] = -requestId;
break;
case 3:
ALOGD("not generating a property change event because of lshal prop: %s",
toString(*response).c_str());
return android::base::Error(static_cast<int>(StatusCode::NOT_AVAILABLE))
<< "not generating a property change event because of lshal prop: "
<< toString(*response);
default:
ALOGE("invalid action on lshal response: %s", toString(*response).c_str());
return android::base::Error(static_cast<int>(StatusCode::INTERNAL_ERROR))
<< "invalid action on lshal response: " << toString(*response);
}
ALOGD("updating property to: %s", toString(*response).c_str());
return response;
}
void EmulatedUserHal::showDumpHelp(int fd) {
dprintf(fd, "%s: dumps state used for user management\n", kUserHalDumpOption);
}
void EmulatedUserHal::dump(int fd, std::string indent) {
if (mInitialUserResponseFromCmd != nullptr) {
dprintf(fd, "%sInitialUserInfo response: %s\n", indent.c_str(),
toString(*mInitialUserResponseFromCmd).c_str());
} else {
dprintf(fd, "%sNo InitialUserInfo response\n", indent.c_str());
}
if (mSwitchUserResponseFromCmd != nullptr) {
dprintf(fd, "%sSwitchUser response: %s\n", indent.c_str(),
toString(*mSwitchUserResponseFromCmd).c_str());
} else {
dprintf(fd, "%sNo SwitchUser response\n", indent.c_str());
}
}
} // namespace impl
} // namespace V2_0
} // namespace vehicle
} // namespace automotive
} // namespace hardware
} // namespace android

View file

@ -0,0 +1,115 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef android_hardware_automotive_vehicle_V2_0_impl_EmulatedUserHal_H_
#define android_hardware_automotive_vehicle_V2_0_impl_EmulatedUserHal_H_
#include <android-base/result.h>
#include <android/hardware/automotive/vehicle/2.0/types.h>
namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {
namespace V2_0 {
namespace impl {
constexpr char kUserHalDumpOption[] = "--user-hal";
/**
* Class used to emulate User HAL behavior through lshal debug requests.
*/
class EmulatedUserHal {
public:
EmulatedUserHal() {}
~EmulatedUserHal() = default;
/**
* Checks if the emulator can handle the property.
*/
bool isSupported(int32_t prop);
/**
* Lets the emulator handle the property.
*
* @return updated property and StatusCode
*/
android::base::Result<std::unique_ptr<VehiclePropValue>> onSetProperty(
const VehiclePropValue& value);
/**
* Shows the User HAL emulation help.
*/
void showDumpHelp(int fd);
/**
* Dump its contents.
*/
void dump(int fd, std::string indent);
private:
/**
* INITIAL_USER_INFO is called by Android when it starts, and it's expecting a property change
* indicating what the initial user should be.
*
* During normal circumstances, the emulator will reply right away, passing a response if
* InitialUserInfoResponseAction::DEFAULT (so Android could use its own logic to decide which
* user to boot).
*
* But during development / testing, the behavior can be changed using lshal dump, which must
* use the areaId to indicate what should happen next.
*
* So, the behavior of set(INITIAL_USER_INFO) is:
*
* - if it has an areaId, store the property into mInitialUserResponseFromCmd (as it was called
* by lshal).
* - else if mInitialUserResponseFromCmd is not set, return a response with the same request id
* and InitialUserInfoResponseAction::DEFAULT
* - else the behavior is defined by the areaId on mInitialUserResponseFromCmd:
* - if it's 1, reply with mInitialUserResponseFromCmd and the right request id
* - if it's 2, reply with mInitialUserResponseFromCmd but a wrong request id (so Android can
* test this error scenario)
* - if it's 3, then don't send a property change (so Android can emulate a timeout)
*
*/
android::base::Result<std::unique_ptr<VehiclePropValue>> onSetInitialUserInfoResponse(
const VehiclePropValue& value);
/**
* Used to emulate SWITCH_USER - see onSetInitialUserInfoResponse() for usage.
*/
android::base::Result<std::unique_ptr<VehiclePropValue>> onSetSwitchUserResponse(
const VehiclePropValue& value);
android::base::Result<std::unique_ptr<VehiclePropValue>> sendUserHalResponse(
std::unique_ptr<VehiclePropValue> response, int32_t requestId);
std::unique_ptr<VehiclePropValue> mInitialUserResponseFromCmd;
std::unique_ptr<VehiclePropValue> mSwitchUserResponseFromCmd;
};
} // namespace impl
} // namespace V2_0
} // namespace vehicle
} // namespace automotive
} // namespace hardware
} // namespace android
#endif // android_hardware_automotive_vehicle_V2_0_impl_EmulatedUserHal_H_

View file

@ -38,9 +38,6 @@ namespace impl {
class EmulatedPassthroughConnector : public PassthroughConnector {
public:
bool onDump(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;
private:
void dumpUserHal(int fd, std::string indent);
};
bool EmulatedPassthroughConnector::onDump(const hidl_handle& handle,
@ -50,12 +47,12 @@ bool EmulatedPassthroughConnector::onDump(const hidl_handle& handle,
if (options.size() > 0) {
if (options[0] == "--help") {
dprintf(fd, "Emulator-specific usage:\n");
dprintf(fd, "--user-hal: dumps state used for user management \n");
mEmulatedUserHal.showDumpHelp(fd);
dprintf(fd, "\n");
// Include caller's help options
return true;
} else if (options[0] == "--user-hal") {
dumpUserHal(fd, "");
} else if (options[0] == kUserHalDumpOption) {
mEmulatedUserHal.dump(fd, "");
return false;
} else {
@ -65,27 +62,12 @@ bool EmulatedPassthroughConnector::onDump(const hidl_handle& handle,
}
dprintf(fd, "Emulator-specific state:\n");
dumpUserHal(fd, " ");
mEmulatedUserHal.dump(fd, " ");
dprintf(fd, "\n");
return true;
}
void EmulatedPassthroughConnector::dumpUserHal(int fd, std::string indent) {
if (mInitialUserResponseFromCmd != nullptr) {
dprintf(fd, "%sInitialUserInfo response: %s\n", indent.c_str(),
toString(*mInitialUserResponseFromCmd).c_str());
} else {
dprintf(fd, "%sNo InitialUserInfo response\n", indent.c_str());
}
if (mSwitchUserResponseFromCmd != nullptr) {
dprintf(fd, "%sSwitchUser response: %s\n", indent.c_str(),
toString(*mSwitchUserResponseFromCmd).c_str());
} else {
dprintf(fd, "%sNo SwitchUser response\n", indent.c_str());
}
}
PassthroughConnectorPtr makeEmulatedPassthroughConnector() {
return std::make_unique<EmulatedPassthroughConnector>();
}

View file

@ -181,6 +181,23 @@ VehicleHalServer::VehiclePropValuePtr VehicleHalServer::createHwInputKeyProp(
}
StatusCode VehicleHalServer::onSetProperty(const VehiclePropValue& value, bool updateStatus) {
if (mEmulatedUserHal.isSupported(value.prop)) {
LOG(INFO) << "onSetProperty(): property " << value.prop << " will be handled by UserHal";
const auto& ret = mEmulatedUserHal.onSetProperty(value);
if (!ret.ok()) {
LOG(ERROR) << "onSetProperty(): HAL returned error: " << ret.error().message();
return StatusCode(ret.error().code());
}
auto updatedValue = ret.value().get();
if (updatedValue != nullptr) {
LOG(INFO) << "onSetProperty(): updating property returned by HAL: "
<< toString(*updatedValue);
onPropertyValueFromCar(*updatedValue, updateStatus);
}
return StatusCode::OK;
}
// Some properties need to be treated non-trivially
switch (value.prop) {
case kGenerateFakeDataControllingProperty:
@ -245,10 +262,6 @@ StatusCode VehicleHalServer::onSetProperty(const VehiclePropValue& value, bool u
break;
}
break;
case INITIAL_USER_INFO:
return onSetInitialUserInfoResponse(value, updateStatus);
case SWITCH_USER:
return onSetSwitchUserResponse(value, updateStatus);
default:
break;
}
@ -262,165 +275,4 @@ StatusCode VehicleHalServer::onSetProperty(const VehiclePropValue& value, bool u
return StatusCode::OK;
}
/**
* INITIAL_USER_INFO is called by Android when it starts, and it's expecting a property change
* indicating what the initial user should be.
*
* During normal circumstances, the emulator will reply right away, passing a response if
* InitialUserInfoResponseAction::DEFAULT (so Android could use its own logic to decide which user
* to boot).
*
* But during development / testing, the behavior can be changed using lshal dump, which must use
* the areaId to indicate what should happen next.
*
* So, the behavior of set(INITIAL_USER_INFO) is:
*
* - if it has an areaId, store the property into mInitialUserResponseFromCmd (as it was called by
* lshal).
* - else if mInitialUserResponseFromCmd is not set, return a response with the same request id and
* InitialUserInfoResponseAction::DEFAULT
* - else the behavior is defined by the areaId on mInitialUserResponseFromCmd:
* - if it's 1, reply with mInitialUserResponseFromCmd and the right request id
* - if it's 2, reply with mInitialUserResponseFromCmd but a wrong request id (so Android can test
* this error scenario)
* - if it's 3, then don't send a property change (so Android can emulate a timeout)
*
*/
StatusCode VehicleHalServer::onSetInitialUserInfoResponse(const VehiclePropValue& value,
bool updateStatus) {
// TODO: LOG calls below might be more suited to be DEBUG, but those are not being logged
// (even when explicitly calling setprop log.tag. As this class should be using ALOG instead of
// LOG, it's not worth investigating why...
if (value.value.int32Values.size() == 0) {
LOG(ERROR) << "set(INITIAL_USER_INFO): no int32values, ignoring it: " << toString(value);
return StatusCode::INVALID_ARG;
}
if (value.areaId != 0) {
LOG(INFO) << "set(INITIAL_USER_INFO) called from lshal; storing it: " << toString(value);
mInitialUserResponseFromCmd.reset(new VehiclePropValue(value));
return StatusCode::OK;
}
LOG(INFO) << "set(INITIAL_USER_INFO) called from Android: " << toString(value);
int32_t requestId = value.value.int32Values[0];
// Create the update property and set common values
auto updatedValue = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
updatedValue->prop = INITIAL_USER_INFO;
updatedValue->timestamp = elapsedRealtimeNano();
if (mInitialUserResponseFromCmd == nullptr) {
updatedValue->value.int32Values.resize(2);
updatedValue->value.int32Values[0] = requestId;
updatedValue->value.int32Values[1] = (int32_t)InitialUserInfoResponseAction::DEFAULT;
LOG(INFO) << "no lshal response; returning InitialUserInfoResponseAction::DEFAULT: "
<< toString(*updatedValue);
onPropertyValueFromCar(*updatedValue, updateStatus);
return StatusCode::OK;
}
// mInitialUserResponseFromCmd is used for just one request
std::unique_ptr<VehiclePropValue> response = std::move(mInitialUserResponseFromCmd);
// TODO(b/150409377): rather than populate the raw values directly, it should use the
// libraries that convert a InitialUserInfoResponse into a VehiclePropValue)
switch (response->areaId) {
case 1:
LOG(INFO) << "returning response with right request id";
*updatedValue = *response;
updatedValue->areaId = 0;
updatedValue->value.int32Values[0] = requestId;
break;
case 2:
LOG(INFO) << "returning response with wrong request id";
*updatedValue = *response;
updatedValue->areaId = 0;
updatedValue->value.int32Values[0] = -requestId;
break;
case 3:
LOG(INFO) << "not generating a property change event because of lshal prop: "
<< toString(*response);
return StatusCode::OK;
default:
LOG(ERROR) << "invalid action on lshal response: " << toString(*response);
return StatusCode::INTERNAL_ERROR;
}
LOG(INFO) << "updating property to: " << toString(*updatedValue);
onPropertyValueFromCar(*updatedValue, updateStatus);
return StatusCode::OK;
}
/**
* Used to emulate SWITCH_USER - see onSetInitialUserInfoResponse() for usage.
*/
StatusCode VehicleHalServer::onSetSwitchUserResponse(const VehiclePropValue& value,
bool updateStatus) {
if (value.value.int32Values.size() == 0) {
LOG(ERROR) << "set(SWITCH_USER): no int32values, ignoring it: " << toString(value);
return StatusCode::INVALID_ARG;
}
if (value.areaId != 0) {
LOG(INFO) << "set(SWITCH_USER) called from lshal; storing it: " << toString(value);
mSwitchUserResponseFromCmd.reset(new VehiclePropValue(value));
return StatusCode::OK;
}
LOG(INFO) << "set(SWITCH_USER) called from Android: " << toString(value);
int32_t requestId = value.value.int32Values[0];
// Create the update property and set common values
auto updatedValue = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
updatedValue->prop = SWITCH_USER;
updatedValue->timestamp = elapsedRealtimeNano();
if (mSwitchUserResponseFromCmd == nullptr) {
updatedValue->value.int32Values.resize(3);
updatedValue->value.int32Values[0] = requestId;
updatedValue->value.int32Values[1] = (int32_t)SwitchUserMessageType::VEHICLE_RESPONSE;
updatedValue->value.int32Values[2] = (int32_t)SwitchUserStatus::SUCCESS;
LOG(INFO) << "no lshal response; returning VEHICLE_RESPONSE / SUCCESS: "
<< toString(*updatedValue);
onPropertyValueFromCar(*updatedValue, updateStatus);
return StatusCode::OK;
}
// mSwitchUserResponseFromCmd is used for just one request
std::unique_ptr<VehiclePropValue> response = std::move(mSwitchUserResponseFromCmd);
// TODO(b/150409377): move code below to a local function like sendUserHalResponse(),
// as it's the same for all (like onSetInitialUserInfoResponse)
switch (response->areaId) {
case 1:
LOG(INFO) << "returning response with right request id";
*updatedValue = *response;
updatedValue->areaId = 0;
updatedValue->value.int32Values[0] = requestId;
break;
case 2:
LOG(INFO) << "returning response with wrong request id";
*updatedValue = *response;
updatedValue->areaId = 0;
updatedValue->value.int32Values[0] = -requestId;
break;
case 3:
LOG(INFO) << "not generating a property change event because of lshal prop: "
<< toString(*response);
return StatusCode::OK;
default:
LOG(ERROR) << "invalid action on lshal response: " << toString(*response);
return StatusCode::INTERNAL_ERROR;
}
LOG(INFO) << "updating property to: " << toString(*updatedValue);
onPropertyValueFromCar(*updatedValue, updateStatus);
return StatusCode::OK;
}
} // namespace android::hardware::automotive::vehicle::V2_0::impl

View file

@ -19,6 +19,7 @@
#include <vhal_v2_0/VehicleObjectPool.h>
#include <vhal_v2_0/VehicleServer.h>
#include "EmulatedUserHal.h"
#include "GeneratorHub.h"
namespace android::hardware::automotive::vehicle::V2_0::impl {
@ -53,15 +54,10 @@ class VehicleHalServer : public IVehicleServer {
VehiclePropValuePtr createHwInputKeyProp(VehicleHwKeyInputAction action, int32_t keyCode,
int32_t targetDisplay);
StatusCode onSetInitialUserInfoResponse(const VehiclePropValue& value, bool updateStatus);
StatusCode onSetSwitchUserResponse(const VehiclePropValue& value, bool updateStatus);
// data members
protected:
// TODO(b/146207078): it might be clearer to move members below to an EmulatedUserHal class
std::unique_ptr<VehiclePropValue> mInitialUserResponseFromCmd;
std::unique_ptr<VehiclePropValue> mSwitchUserResponseFromCmd;
EmulatedUserHal mEmulatedUserHal;
private:
GeneratorHub mGeneratorHub{