diff --git a/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp b/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp index 4249a61107..4f42e630f7 100644 --- a/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp +++ b/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp @@ -28,6 +28,8 @@ #include +#include + #include "VehicleUtils.h" // TODO: figure out how to include private/android_filesystem_config.h instead... @@ -247,10 +249,11 @@ void VehicleHalManager::cmdHelp(int fd) const { dprintf(fd, "--get [PROP2] [PROPN]: dumps the value of specific properties \n"); // TODO: support other formats (int64, float, bytes) dprintf(fd, - "--set [ ]: sets the value of property PROP, using" - " arbitrary number of key/value parameters (i for int32, s for string). Notice that " - "the string value can be set just once, while the other can have multiple values " - "(so they're used in the respective array)\n"); + "--set [ ] [a AREA_ID] : sets the value of " + "property PROP, using arbitrary number of key/value parameters (i for int32, " + "s for string) and an optional area.\n" + "Notice that the string value can be set just once, while the other can have multiple " + "values (so they're used in the respective array)\n"); } void VehicleHalManager::cmdListAllProperties(int fd) const { @@ -352,13 +355,13 @@ void VehicleHalManager::cmdSetOneProperty(int fd, const hidl_vec& o VehiclePropValue prop; if (!safelyParseInt(fd, 1, options[1], &prop.prop)) return; - prop.timestamp = 0; - prop.areaId = 0; // TODO: add option to pass areaId as parameter + prop.timestamp = elapsedRealtimeNano(); prop.status = VehiclePropertyStatus::AVAILABLE; - // First pass calculate sizes + // First pass: calculate sizes int sizeInt32 = 0; int stringIndex = 0; + int areaIndex = 0; for (int i = 2, kv = 1; kv <= numberValues; kv++) { // iterate through the kv=1..n key/value pairs, accessing indexes i / i+1 at each step std::string type = options[i]; @@ -374,6 +377,15 @@ void VehicleHalManager::cmdSetOneProperty(int fd, const hidl_vec& o return; } stringIndex = i; + } else if (EqualsIgnoreCase(type, "a")) { + if (areaIndex != 0) { + dprintf(fd, + "defining area value (%s) again at index %d (already defined at %d=%s" + ")\n", + value.c_str(), i, areaIndex, options[areaIndex + 1].c_str()); + return; + } + areaIndex = i; } else { dprintf(fd, "invalid (%s) type at index %d\n", type.c_str(), i); return; @@ -395,6 +407,8 @@ void VehicleHalManager::cmdSetOneProperty(int fd, const hidl_vec& o prop.value.int32Values[indexInt32++] = safeInt; } else if (EqualsIgnoreCase(type, "s")) { prop.value.stringValue = value; + } else if (EqualsIgnoreCase(type, "a")) { + if (!safelyParseInt(fd, valueIndex, value, &prop.areaId)) return; } i += 2; } diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h index e19987c31c..785f0e0724 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h @@ -76,6 +76,7 @@ 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; /** * This property is used for test purpose to generate fake events. Here is the test package that @@ -990,6 +991,16 @@ const ConfigDeclaration kVehicleProperties[]{ (int)VehicleVendorPermission::PERMISSION_DEFAULT}, }, .initialValue = {.int32Values = {1}}}, + + { + .config = + { + .prop = toInt(VehicleProperty::INITIAL_USER_INFO), + .access = VehiclePropertyAccess::READ_WRITE, + .changeMode = VehiclePropertyChangeMode::ON_CHANGE, + }, + }, + }; } // impl diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp index d9867bd39e..197c6dbedb 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp @@ -13,6 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +#define LOG_TAG "automotive.vehicle@2.0-connector" + #include #include @@ -261,6 +264,8 @@ StatusCode EmulatedVehicleServer::onSetProperty(const VehiclePropValue& value, b break; } break; + case INITIAL_USER_INFO: + return onSetInitialUserInfo(value, updateStatus); default: break; } @@ -274,6 +279,97 @@ StatusCode EmulatedVehicleServer::onSetProperty(const VehiclePropValue& value, b 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 EmulatedVehicleServer::onSetInitialUserInfo(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.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); + + if (value.value.int32Values.size() == 0) { + LOG(ERROR) << "invalid request (no requestId): " << toString(value); + return StatusCode::INVALID_ARG; + } + 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 response = std::move(mInitialUserResponseFromCmd); + + // TODO(b/138709788): 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; +} + EmulatedPassthroughConnectorPtr makeEmulatedPassthroughConnector() { return std::make_unique(); } diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.h index 5fc6493ba5..6d927b06e2 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.h +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.h @@ -77,6 +77,10 @@ class EmulatedVehicleServer : public IVehicleServer { std::bind(&EmulatedVehicleServer::onFakeValueGenerated, this, std::placeholders::_1)}; VehiclePropValuePool* mValuePool{nullptr}; + + // TODO(b/146207078): it might be clearer to move members below to an EmulatedUserHal class + std::unique_ptr mInitialUserResponseFromCmd; + StatusCode onSetInitialUserInfo(const VehiclePropValue& value, bool updateStatus); }; // Helper functions