Move emulatedUserHal to default VHAL.
Test: Run on emulator. Verify user hal logic works. unit test.
Bug: 193831021
Change-Id: I9b91205ac11837f31950018f2de12542df85e202
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index 8bf6bbe..2c3422e 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -97,6 +97,7 @@
local_include_dirs: ["common/include/vhal_v2_0"],
export_include_dirs: ["impl"],
whole_static_libs: [
+ "android.hardware.automotive.vehicle@2.0-emulated-user-hal-lib",
"android.hardware.automotive.vehicle@2.0-manager-lib",
],
shared_libs: [
@@ -215,8 +216,8 @@
"android.hardware.automotive.vehicle@2.0-libproto-native",
],
data: [
- ":vhal_test_override_json",
":vhal_test_json",
+ ":vhal_test_override_json",
],
test_suites: ["general-tests"],
}
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp
index 74337b8..25a698b 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp
@@ -89,11 +89,38 @@
});
}
+VehicleHal::VehiclePropValuePtr DefaultVehicleHal::getUserHalProp(
+ const VehiclePropValue& requestedPropValue, StatusCode* outStatus) {
+ auto propId = requestedPropValue.prop;
+ ALOGI("get(): getting value for prop %d from User HAL", propId);
+ const auto& ret = mEmulatedUserHal.onGetProperty(requestedPropValue);
+ VehicleHal::VehiclePropValuePtr v = nullptr;
+ if (!ret.ok()) {
+ ALOGE("get(): User HAL returned error: %s", ret.error().message().c_str());
+ *outStatus = StatusCode(ret.error().code());
+ } else {
+ auto value = ret.value().get();
+ if (value != nullptr) {
+ ALOGI("get(): User HAL returned value: %s", toString(*value).c_str());
+ v = getValuePool()->obtain(*value);
+ *outStatus = StatusCode::OK;
+ } else {
+ ALOGE("get(): User HAL returned null value");
+ *outStatus = StatusCode::INTERNAL_ERROR;
+ }
+ }
+ return addTimestamp(std::move(v));
+}
+
VehicleHal::VehiclePropValuePtr DefaultVehicleHal::get(const VehiclePropValue& requestedPropValue,
StatusCode* outStatus) {
auto propId = requestedPropValue.prop;
ALOGV("get(%d)", propId);
+ if (mEmulatedUserHal.isSupported(propId)) {
+ return getUserHalProp(requestedPropValue, outStatus);
+ }
+
VehiclePropValuePtr v = nullptr;
if (propId == OBD2_FREEZE_FRAME) {
v = getValuePool()->obtainComplex();
@@ -127,6 +154,36 @@
}
bool DefaultVehicleHal::dump(const hidl_handle& fd, const hidl_vec<hidl_string>& options) {
+ int nativeFd = fd->data[0];
+ if (nativeFd < 0) {
+ ALOGW("Invalid fd from HIDL handle: %d", nativeFd);
+ return false;
+ }
+ if (options.size() > 0) {
+ if (options[0] == "--help") {
+ std::string buffer;
+ buffer += "Emulated user hal usage:\n";
+ buffer += mEmulatedUserHal.showDumpHelp();
+ buffer += "\n";
+ buffer += "VHAL server debug usage:\n";
+ buffer += "--debughal: send debug command to VHAL server, see '--debughal --help'\n";
+ buffer += "\n";
+ dprintf(nativeFd, "%s", buffer.c_str());
+ return false;
+ } else if (options[0] == kUserHalDumpOption) {
+ dprintf(nativeFd, "%s", mEmulatedUserHal.dump("").c_str());
+ return false;
+ }
+ } else {
+ // No options, dump the emulated user hal state first and then send command to VHAL server
+ // to dump its state.
+ std::string buffer;
+ buffer += "Emulator user hal state:\n";
+ buffer += mEmulatedUserHal.dump(" ");
+ buffer += "\n";
+ dprintf(nativeFd, "%s", buffer.c_str());
+ }
+
return mVehicleClient->dump(fd, options);
}
@@ -285,6 +342,23 @@
return StatusCode::OK;
}
+StatusCode DefaultVehicleHal::setUserHalProp(const VehiclePropValue& propValue) {
+ ALOGI("onSetProperty(): property %d will be handled by UserHal", propValue.prop);
+
+ const auto& ret = mEmulatedUserHal.onSetProperty(propValue);
+ if (!ret.ok()) {
+ ALOGE("onSetProperty(): HAL returned error: %s", ret.error().message().c_str());
+ return StatusCode(ret.error().code());
+ }
+ auto updatedValue = ret.value().get();
+ if (updatedValue != nullptr) {
+ ALOGI("onSetProperty(): updating property returned by HAL: %s",
+ toString(*updatedValue).c_str());
+ onPropertyValue(*updatedValue, true);
+ }
+ return StatusCode::OK;
+}
+
StatusCode DefaultVehicleHal::set(const VehiclePropValue& propValue) {
if (propValue.status != VehiclePropertyStatus::AVAILABLE) {
// Android side cannot set property status - this value is the
@@ -293,6 +367,10 @@
return StatusCode::INVALID_ARG;
}
+ if (mEmulatedUserHal.isSupported(propValue.prop)) {
+ return setUserHalProp(propValue);
+ }
+
std::unordered_set<int32_t> powerProps(std::begin(kHvacPowerProperties),
std::end(kHvacPowerProperties));
if (powerProps.count(propValue.prop)) {
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h
index 027dc85..7cd7ac2 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h
@@ -21,6 +21,7 @@
#include <vhal_v2_0/VehicleHal.h>
#include <vhal_v2_0/VehiclePropertyStore.h>
+#include "EmulatedUserHal.h"
#include "VehicleHalClient.h"
namespace android {
@@ -55,6 +56,7 @@
VehiclePropertyStore* mPropStore;
RecurrentTimer mRecurrentTimer;
VehicleHalClient* mVehicleClient;
+ EmulatedUserHal mEmulatedUserHal;
// The callback that would be called when a property value is updated. This function could
// be extended to handle specific property update event.
@@ -79,6 +81,11 @@
// Register the heart beat event to be sent every 3s. This is required to inform watch dog that
// VHAL is alive. Subclasses should always calls this function during onCreate.
void registerHeartBeatEvent();
+ // Get a user HAL property.
+ VehiclePropValuePtr getUserHalProp(const VehiclePropValue& requestedPropValue,
+ StatusCode* outStatus);
+ // Set a user HAL property.
+ StatusCode setUserHalProp(const VehiclePropValue& propValue);
// Create a VHAL heart beat property.
VehicleHal::VehiclePropValuePtr createVhalHeartBeatProp();
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
index 27c1f4d..2268df8 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
@@ -1047,4 +1047,220 @@
ASSERT_EQ(StatusCode::INVALID_ARG, status);
}
+TEST_F(DefaultVhalImplTest, testGetUserPropertySetOnly) {
+ VehiclePropValue value;
+ value.prop = toInt(VehicleProperty::INITIAL_USER_INFO);
+ StatusCode status;
+
+ mHal->get(value, &status);
+
+ ASSERT_EQ(StatusCode::INVALID_ARG, status);
+
+ value.prop = toInt(VehicleProperty::SWITCH_USER);
+
+ mHal->get(value, &status);
+
+ ASSERT_EQ(StatusCode::INVALID_ARG, status);
+
+ value.prop = toInt(VehicleProperty::CREATE_USER);
+
+ mHal->get(value, &status);
+
+ ASSERT_EQ(StatusCode::INVALID_ARG, status);
+
+ value.prop = toInt(VehicleProperty::REMOVE_USER);
+
+ mHal->get(value, &status);
+
+ ASSERT_EQ(StatusCode::INVALID_ARG, status);
+}
+
+TEST_F(DefaultVhalImplTest, testGetUserIdAssoc) {
+ VehiclePropValue value;
+ value.prop = toInt(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION);
+ StatusCode status;
+
+ mHal->get(value, &status);
+
+ // Default returns NOT_AVAILABLE.
+ ASSERT_EQ(StatusCode::NOT_AVAILABLE, status);
+
+ // This is the same example as used in User HAL Emulation doc.
+ VehiclePropValue setValue = {
+ .prop = toInt(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION),
+ .areaId = 1,
+ .value.int32Values = {666, 1, 1, 2},
+ };
+
+ status = mHal->set(setValue);
+
+ ASSERT_EQ(StatusCode::OK, status);
+
+ auto gotValue = mHal->get(value, &status);
+
+ ASSERT_EQ(StatusCode::OK, status);
+ ASSERT_EQ((size_t)4, gotValue->value.int32Values.size());
+ EXPECT_EQ(1, gotValue->areaId);
+ EXPECT_EQ(666, gotValue->value.int32Values[0]);
+ EXPECT_EQ(1, gotValue->value.int32Values[1]);
+ EXPECT_EQ(1, gotValue->value.int32Values[2]);
+ EXPECT_EQ(2, gotValue->value.int32Values[3]);
+ EXPECT_EQ(toInt(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION), gotValue->prop);
+}
+
+TEST_F(DefaultVhalImplTest, testSwitchUser) {
+ // This is the same example as used in User HAL Emulation doc.
+ VehiclePropValue setValue = {
+ .prop = toInt(VehicleProperty::SWITCH_USER),
+ .areaId = 1,
+ .value.int32Values = {666, 3, 2},
+ };
+
+ auto status = mHal->set(setValue);
+
+ ASSERT_EQ(StatusCode::OK, status);
+
+ // Simulate a request from Android side.
+ setValue = {
+ .prop = toInt(VehicleProperty::SWITCH_USER),
+ .areaId = 0,
+ .value.int32Values = {666, 3},
+ };
+ // Clear existing events.
+ mEventQueue.flush();
+
+ status = mHal->set(setValue);
+
+ ASSERT_EQ(StatusCode::OK, status);
+
+ // Should generate an event for user hal response.
+ auto events = mEventQueue.flush();
+ ASSERT_EQ((size_t)1, events.size());
+ EXPECT_EQ(1, events[0]->areaId);
+ EXPECT_EQ(toInt(VehicleProperty::SWITCH_USER), events[0]->prop);
+ ASSERT_EQ((size_t)3, events[0]->value.int32Values.size());
+ EXPECT_EQ(666, events[0]->value.int32Values[0]);
+ EXPECT_EQ(3, events[0]->value.int32Values[1]);
+ EXPECT_EQ(2, events[0]->value.int32Values[2]);
+
+ // Try to get switch_user again, should return default value.
+ status = mHal->set(setValue);
+ ASSERT_EQ(StatusCode::OK, status);
+
+ events = mEventQueue.flush();
+ ASSERT_EQ((size_t)1, events.size());
+ EXPECT_EQ(0, events[0]->areaId);
+ EXPECT_EQ(toInt(VehicleProperty::SWITCH_USER), events[0]->prop);
+ ASSERT_EQ((size_t)3, events[0]->value.int32Values.size());
+ // Request ID
+ EXPECT_EQ(666, events[0]->value.int32Values[0]);
+ // VEHICLE_RESPONSE
+ EXPECT_EQ(3, events[0]->value.int32Values[1]);
+ // SUCCESS
+ EXPECT_EQ(1, events[0]->value.int32Values[2]);
+}
+
+TEST_F(DefaultVhalImplTest, testCreateUser) {
+ // This is the same example as used in User HAL Emulation doc.
+ VehiclePropValue setValue = {
+ .prop = toInt(VehicleProperty::CREATE_USER),
+ .areaId = 1,
+ .value.int32Values = {666, 2},
+ };
+
+ auto status = mHal->set(setValue);
+
+ ASSERT_EQ(StatusCode::OK, status);
+
+ // Simulate a request from Android side.
+ setValue = {
+ .prop = toInt(VehicleProperty::CREATE_USER),
+ .areaId = 0,
+ .value.int32Values = {666},
+ };
+ // Clear existing events.
+ mEventQueue.flush();
+
+ status = mHal->set(setValue);
+
+ ASSERT_EQ(StatusCode::OK, status);
+
+ // Should generate an event for user hal response.
+ auto events = mEventQueue.flush();
+ ASSERT_EQ((size_t)1, events.size());
+ EXPECT_EQ(1, events[0]->areaId);
+ EXPECT_EQ(toInt(VehicleProperty::CREATE_USER), events[0]->prop);
+ ASSERT_EQ((size_t)2, events[0]->value.int32Values.size());
+ EXPECT_EQ(666, events[0]->value.int32Values[0]);
+ EXPECT_EQ(2, events[0]->value.int32Values[1]);
+
+ // Try to get create_user again, should return default value.
+ status = mHal->set(setValue);
+ ASSERT_EQ(StatusCode::OK, status);
+
+ events = mEventQueue.flush();
+ ASSERT_EQ((size_t)1, events.size());
+ EXPECT_EQ(0, events[0]->areaId);
+ EXPECT_EQ(toInt(VehicleProperty::CREATE_USER), events[0]->prop);
+ ASSERT_EQ((size_t)2, events[0]->value.int32Values.size());
+ // Request ID
+ EXPECT_EQ(666, events[0]->value.int32Values[0]);
+ // SUCCESS
+ EXPECT_EQ(1, events[0]->value.int32Values[1]);
+}
+
+TEST_F(DefaultVhalImplTest, testInitialUserInfo) {
+ // This is the same example as used in User HAL Emulation doc.
+ VehiclePropValue setValue = {
+ .prop = toInt(VehicleProperty::INITIAL_USER_INFO),
+ .areaId = 1,
+ .value.int32Values = {666, 1, 11},
+ };
+
+ auto status = mHal->set(setValue);
+
+ ASSERT_EQ(StatusCode::OK, status);
+
+ // Simulate a request from Android side.
+ setValue = {
+ .prop = toInt(VehicleProperty::INITIAL_USER_INFO),
+ .areaId = 0,
+ .value.int32Values = {3},
+ };
+ // Clear existing events.
+ mEventQueue.flush();
+
+ status = mHal->set(setValue);
+
+ ASSERT_EQ(StatusCode::OK, status);
+
+ // Should generate an event for user hal response.
+ auto events = mEventQueue.flush();
+ ASSERT_EQ((size_t)1, events.size());
+ EXPECT_EQ(1, events[0]->areaId);
+ EXPECT_EQ(toInt(VehicleProperty::INITIAL_USER_INFO), events[0]->prop);
+ ASSERT_EQ((size_t)3, events[0]->value.int32Values.size());
+ EXPECT_EQ(3, events[0]->value.int32Values[0]);
+ EXPECT_EQ(1, events[0]->value.int32Values[1]);
+ EXPECT_EQ(11, events[0]->value.int32Values[2]);
+
+ // Try to get create_user again, should return default value.
+ status = mHal->set(setValue);
+ ASSERT_EQ(StatusCode::OK, status);
+
+ events = mEventQueue.flush();
+ ASSERT_EQ((size_t)1, events.size());
+ EXPECT_EQ(0, events[0]->areaId);
+ EXPECT_EQ(toInt(VehicleProperty::INITIAL_USER_INFO), events[0]->prop);
+ ASSERT_EQ((size_t)4, events[0]->value.int32Values.size());
+ // Request ID
+ EXPECT_EQ(3, events[0]->value.int32Values[0]);
+ // ACTION: DEFAULT
+ EXPECT_EQ(0, events[0]->value.int32Values[1]);
+ // User id: 0
+ EXPECT_EQ(0, events[0]->value.int32Values[2]);
+ // Flags: 0
+ EXPECT_EQ(0, events[0]->value.int32Values[3]);
+}
+
} // namespace
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/Android.bp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/Android.bp
new file mode 100644
index 0000000..0058d33
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/Android.bp
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Library used to emulate User HAL behavior through lshal debug requests.
+cc_library {
+ name: "android.hardware.automotive.vehicle@2.0-emulated-user-hal-lib",
+ vendor: true,
+ defaults: ["vhal_v2_0_target_defaults"],
+ srcs: ["EmulatedUserHal.cpp"],
+ shared_libs: [
+ "libbase",
+ "libutils",
+ "libcutils",
+ ],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+ whole_static_libs: [
+ "android.hardware.automotive.vehicle@2.0-user-hal-helper-lib",
+ ],
+}
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/EmulatedUserHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/EmulatedUserHal.cpp
new file mode 100644
index 0000000..55dc01a
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/EmulatedUserHal.cpp
@@ -0,0 +1,353 @@
+/*
+ * 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 "UserHalHelper.h"
+
+#include "EmulatedUserHal.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+namespace {
+
+using android::base::Error;
+using android::base::Result;
+
+constexpr int32_t INITIAL_USER_INFO = static_cast<int32_t>(VehicleProperty::INITIAL_USER_INFO);
+constexpr int32_t SWITCH_USER = static_cast<int32_t>(VehicleProperty::SWITCH_USER);
+constexpr int32_t CREATE_USER = static_cast<int32_t>(VehicleProperty::CREATE_USER);
+constexpr int32_t REMOVE_USER = static_cast<int32_t>(VehicleProperty::REMOVE_USER);
+constexpr int32_t USER_IDENTIFICATION_ASSOCIATION =
+ static_cast<int32_t>(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION);
+
+Result<int32_t> getRequestId(const VehiclePropValue& value) {
+ if (value.value.int32Values.size() < 1) {
+ return Error(static_cast<int>(StatusCode::INVALID_ARG))
+ << "no int32values on " << toString(value);
+ }
+ return value.value.int32Values[0];
+}
+
+Result<SwitchUserMessageType> getSwitchUserMessageType(const VehiclePropValue& value) {
+ if (value.value.int32Values.size() < 2) {
+ return Error(static_cast<int>(StatusCode::INVALID_ARG))
+ << "missing switch user message type " << toString(value);
+ }
+ return user_hal_helper::verifyAndCast<SwitchUserMessageType>(value.value.int32Values[1]);
+}
+
+} // namespace
+
+bool EmulatedUserHal::isSupported(int32_t prop) {
+ switch (prop) {
+ case INITIAL_USER_INFO:
+ case SWITCH_USER:
+ case CREATE_USER:
+ case REMOVE_USER:
+ case USER_IDENTIFICATION_ASSOCIATION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+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);
+ case CREATE_USER:
+ return onSetCreateUserResponse(value);
+ case REMOVE_USER:
+ ALOGI("REMOVE_USER is FYI only, nothing to do...");
+ return {};
+ case USER_IDENTIFICATION_ASSOCIATION:
+ return onSetUserIdentificationAssociation(value);
+ default:
+ return Error(static_cast<int>(StatusCode::INVALID_ARG))
+ << "Unsupported property: " << toString(value);
+ }
+}
+
+Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onGetProperty(
+ const VehiclePropValue& value) {
+ ALOGV("onGetProperty(%s)", toString(value).c_str());
+ switch (value.prop) {
+ case INITIAL_USER_INFO:
+ case SWITCH_USER:
+ case CREATE_USER:
+ case REMOVE_USER:
+ ALOGE("onGetProperty(): %d is only supported on SET", value.prop);
+ return Error(static_cast<int>(StatusCode::INVALID_ARG)) << "only supported on SET";
+ case USER_IDENTIFICATION_ASSOCIATION:
+ return onGetUserIdentificationAssociation(value);
+ default:
+ ALOGE("onGetProperty(): %d is not supported", value.prop);
+ return Error(static_cast<int>(StatusCode::INVALID_ARG)) << "not supported by User HAL";
+ }
+}
+
+Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onGetUserIdentificationAssociation(
+ const VehiclePropValue& value) {
+ if (mSetUserIdentificationAssociationResponseFromCmd == nullptr) {
+ return defaultUserIdentificationAssociation(value);
+ }
+ ALOGI("get(USER_IDENTIFICATION_ASSOCIATION): returning %s",
+ toString(*mSetUserIdentificationAssociationResponseFromCmd).c_str());
+ auto newValue = std::unique_ptr<VehiclePropValue>(
+ new VehiclePropValue(*mSetUserIdentificationAssociationResponseFromCmd));
+ auto requestId = getRequestId(value);
+ if (requestId.ok()) {
+ // Must use the same requestId
+ newValue->value.int32Values[0] = *requestId;
+ } else {
+ ALOGE("get(USER_IDENTIFICATION_ASSOCIATION): no requestId on %s", toString(value).c_str());
+ }
+ return newValue;
+}
+
+Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onSetInitialUserInfoResponse(
+ const VehiclePropValue& value) {
+ auto requestId = getRequestId(value);
+ if (!requestId.ok()) {
+ ALOGE("Failed to get requestId on set(INITIAL_USER_INFO): %s",
+ requestId.error().message().c_str());
+ return requestId.error();
+ }
+
+ 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());
+ 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 = user_hal_helper::toVehiclePropValue(InitialUserInfoResponse{
+ .requestId = *requestId,
+ .action = InitialUserInfoResponseAction::DEFAULT,
+ });
+ ALOGI("no lshal response; replying with InitialUserInfoResponseAction::DEFAULT: %s",
+ toString(*updatedValue).c_str());
+ return updatedValue;
+}
+
+Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onSetSwitchUserResponse(
+ const VehiclePropValue& value) {
+ auto requestId = getRequestId(value);
+ if (!requestId.ok()) {
+ ALOGE("Failed to get requestId on set(SWITCH_USER): %s",
+ requestId.error().message().c_str());
+ return requestId.error();
+ }
+
+ auto messageType = getSwitchUserMessageType(value);
+ if (!messageType.ok()) {
+ ALOGE("Failed to get messageType on set(SWITCH_USER): %s",
+ messageType.error().message().c_str());
+ return messageType.error();
+ }
+
+ if (value.areaId != 0) {
+ if (*messageType == SwitchUserMessageType::VEHICLE_REQUEST) {
+ // User HAL can also request a user switch, so we need to check it first
+ ALOGD("set(SWITCH_USER) called from lshal to emulate a vehicle request: %s",
+ toString(value).c_str());
+ return std::unique_ptr<VehiclePropValue>(new VehiclePropValue(value));
+ }
+ // Otherwise, we store it
+ 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());
+
+ if (mSwitchUserResponseFromCmd != nullptr) {
+ ALOGI("replying SWITCH_USER with lshal value: %s",
+ toString(*mSwitchUserResponseFromCmd).c_str());
+ return sendUserHalResponse(std::move(mSwitchUserResponseFromCmd), *requestId);
+ }
+
+ if (*messageType == SwitchUserMessageType::LEGACY_ANDROID_SWITCH ||
+ *messageType == SwitchUserMessageType::ANDROID_POST_SWITCH) {
+ ALOGI("request is %s; ignoring it", toString(*messageType).c_str());
+ return {};
+ }
+
+ // Returns default response
+ auto updatedValue = user_hal_helper::toVehiclePropValue(SwitchUserResponse{
+ .requestId = *requestId,
+ .messageType = SwitchUserMessageType::VEHICLE_RESPONSE,
+ .status = SwitchUserStatus::SUCCESS,
+ });
+ ALOGI("no lshal response; replying with VEHICLE_RESPONSE / SUCCESS: %s",
+ toString(*updatedValue).c_str());
+ return updatedValue;
+}
+
+Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onSetCreateUserResponse(
+ const VehiclePropValue& value) {
+ auto requestId = getRequestId(value);
+ if (!requestId.ok()) {
+ ALOGE("Failed to get requestId on set(CREATE_USER): %s",
+ requestId.error().message().c_str());
+ return requestId.error();
+ }
+
+ if (value.areaId != 0) {
+ ALOGD("set(CREATE_USER) called from lshal; storing it: %s", toString(value).c_str());
+ mCreateUserResponseFromCmd.reset(new VehiclePropValue(value));
+ return {};
+ }
+ ALOGD("set(CREATE_USER) called from Android: %s", toString(value).c_str());
+
+ if (mCreateUserResponseFromCmd != nullptr) {
+ ALOGI("replying CREATE_USER with lshal value: %s",
+ toString(*mCreateUserResponseFromCmd).c_str());
+ return sendUserHalResponse(std::move(mCreateUserResponseFromCmd), *requestId);
+ }
+
+ // Returns default response
+ auto updatedValue = user_hal_helper::toVehiclePropValue(CreateUserResponse{
+ .requestId = *requestId,
+ .status = CreateUserStatus::SUCCESS,
+ });
+ ALOGI("no lshal response; replying with SUCCESS: %s", toString(*updatedValue).c_str());
+ return updatedValue;
+}
+
+Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onSetUserIdentificationAssociation(
+ const VehiclePropValue& value) {
+ auto requestId = getRequestId(value);
+ if (!requestId.ok()) {
+ ALOGE("Failed to get requestId on set(USER_IDENTIFICATION_ASSOCIATION): %s",
+ requestId.error().message().c_str());
+ return requestId.error();
+ }
+
+ if (value.areaId != 0) {
+ ALOGD("set(USER_IDENTIFICATION_ASSOCIATION) called from lshal; storing it: %s",
+ toString(value).c_str());
+ mSetUserIdentificationAssociationResponseFromCmd.reset(new VehiclePropValue(value));
+ return {};
+ }
+ ALOGD("set(USER_IDENTIFICATION_ASSOCIATION) called from Android: %s", toString(value).c_str());
+
+ if (mSetUserIdentificationAssociationResponseFromCmd != nullptr) {
+ ALOGI("replying USER_IDENTIFICATION_ASSOCIATION with lshal value: %s",
+ toString(*mSetUserIdentificationAssociationResponseFromCmd).c_str());
+ // Not moving response so it can be used on GET requests
+ auto copy = std::unique_ptr<VehiclePropValue>(
+ new VehiclePropValue(*mSetUserIdentificationAssociationResponseFromCmd));
+ return sendUserHalResponse(std::move(copy), *requestId);
+ }
+ // Returns default response
+ return defaultUserIdentificationAssociation(value);
+}
+
+Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::defaultUserIdentificationAssociation(
+ const VehiclePropValue& request) {
+ // TODO(b/159498909): return a response with NOT_ASSOCIATED_ANY_USER for all requested types
+ ALOGE("no lshal response for %s; replying with NOT_AVAILABLE", toString(request).c_str());
+ return Error(static_cast<int>(StatusCode::NOT_AVAILABLE)) << "not set by lshal";
+}
+
+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 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 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;
+}
+
+std::string EmulatedUserHal::showDumpHelp() {
+ return fmt::format("{}: dumps state used for user management\n", kUserHalDumpOption);
+}
+
+std::string EmulatedUserHal::dump(std::string indent) {
+ std::string info;
+ if (mInitialUserResponseFromCmd != nullptr) {
+ info += fmt::format("{}InitialUserInfo response: {}\n", indent.c_str(),
+ toString(*mInitialUserResponseFromCmd).c_str());
+ } else {
+ info += fmt::format("{}No InitialUserInfo response\n", indent.c_str());
+ }
+ if (mSwitchUserResponseFromCmd != nullptr) {
+ info += fmt::format("{}SwitchUser response: {}\n", indent.c_str(),
+ toString(*mSwitchUserResponseFromCmd).c_str());
+ } else {
+ info += fmt::format("{}No SwitchUser response\n", indent.c_str());
+ }
+ if (mCreateUserResponseFromCmd != nullptr) {
+ info += fmt::format("{}CreateUser response: {}\n", indent.c_str(),
+ toString(*mCreateUserResponseFromCmd).c_str());
+ } else {
+ info += fmt::format("{}No CreateUser response\n", indent.c_str());
+ }
+ if (mSetUserIdentificationAssociationResponseFromCmd != nullptr) {
+ info += fmt::format("{}SetUserIdentificationAssociation response: {}\n", indent.c_str(),
+ toString(*mSetUserIdentificationAssociationResponseFromCmd).c_str());
+ } else {
+ info += fmt::format("{}No SetUserIdentificationAssociation response\n", indent.c_str());
+ }
+ return info;
+}
+
+} // namespace impl
+
+} // namespace V2_0
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/include/EmulatedUserHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/include/EmulatedUserHal.h
new file mode 100644
index 0000000..46f6d0f
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/include/EmulatedUserHal.h
@@ -0,0 +1,152 @@
+/*
+ * 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/format.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 a real 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 set the property.
+ *
+ * @return updated property and StatusCode
+ */
+ android::base::Result<std::unique_ptr<VehiclePropValue>> onSetProperty(
+ const VehiclePropValue& value);
+
+ /**
+ * Gets the property value from the emulator.
+ *
+ * @return property value and StatusCode
+ */
+ android::base::Result<std::unique_ptr<VehiclePropValue>> onGetProperty(
+ const VehiclePropValue& value);
+
+ /**
+ * Shows the User HAL emulation help.
+ */
+ std::string showDumpHelp();
+
+ /**
+ * Dump its contents.
+ */
+ std::string dump(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);
+
+ /**
+ * Used to emulate CREATE_USER - see onSetInitialUserInfoResponse() for usage.
+ */
+ android::base::Result<std::unique_ptr<VehiclePropValue>> onSetCreateUserResponse(
+ const VehiclePropValue& value);
+
+ /**
+ * Used to emulate set USER_IDENTIFICATION_ASSOCIATION - see onSetInitialUserInfoResponse() for
+ * usage.
+ */
+ android::base::Result<std::unique_ptr<VehiclePropValue>> onSetUserIdentificationAssociation(
+ const VehiclePropValue& value);
+
+ /**
+ * Used to emulate get USER_IDENTIFICATION_ASSOCIATION - see onSetInitialUserInfoResponse() for
+ * usage.
+ */
+ android::base::Result<std::unique_ptr<VehiclePropValue>> onGetUserIdentificationAssociation(
+ const VehiclePropValue& value);
+
+ /**
+ * Creates a default USER_IDENTIFICATION_ASSOCIATION when it was not set by lshal.
+ */
+ android::base::Result<std::unique_ptr<VehiclePropValue>> defaultUserIdentificationAssociation(
+ const VehiclePropValue& request);
+
+ 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;
+ std::unique_ptr<VehiclePropValue> mCreateUserResponseFromCmd;
+ std::unique_ptr<VehiclePropValue> mSetUserIdentificationAssociationResponseFromCmd;
+};
+
+} // namespace impl
+
+} // namespace V2_0
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // android_hardware_automotive_vehicle_V2_0_impl_EmulatedUserHal_H_